9

I'd like to convert the JSON string

"{ \"birthday\": \"1988-03-18\", \"address\": { \"state\": 24, \"city\": 8341, \"country\": 1 } }"

to

"{ \"address\": { \"city\": 8341, \"country\": 1, \"state\": 24 }, \"birthday\": \"1988-03-18\" }"

NOTE: I'm not using the sorted version for communication (because the key order doesn't really matter), I need a sorted version to perform local tests (by comparing JSON strings).


EDIT: I4V pointed a solution that uses Json.Net, I would rather use a solution that doesn't need to include any 3rd party library (actually I'm using the built in System.Json in my application)


I posted a gist with the solution provided by I4V + some testing here. Thank you all.

2
  • Hmm... Tempting though it sounds, I'd venture that the better solution would be a slightly deeper inspection of the JSON rather than a string comparision. Given that the enumeration of properties in JS not determined by the spec, the order of object properties should not be relied upon because it's really meaningless to order the properties of a json serialization. ECMA-262, section 12.6.4: The mechanics of enumerating the properties ... is implementation dependent. Commented Jan 19, 2013 at 19:00
  • @spender I agree with you that the JSON key order are meaningless and string comparison shouldn't be used for comparing large/complex JSON objects. But a JSON string sorter may be useful for very specific situations (as mine) Commented Jan 19, 2013 at 21:15

5 Answers 5

20

I will use Json.Net for this

string json = @"{ ""birthday"": ""1988-03-18"", ""address"": { ""state"": 24, ""city"": 8341, ""country"": 1 } }";
var jObj = (JObject)JsonConvert.DeserializeObject(json);
Sort(jObj);
string newJson = jObj.ToString();

void Sort(JObject jObj)
{
    var props = jObj.Properties().ToList();
    foreach (var prop in props)
    {
        prop.Remove();
    }

    foreach (var prop in props.OrderBy(p=>p.Name))
    {
        jObj.Add(prop);
        if(prop.Value is JObject)
            Sort((JObject)prop.Value);
    }
}

EDIT

A try with System.Json but I am not sure about OrderByDescending ( or OrderBy).

var jObj = (System.Json.JsonObject)System.Json.JsonObject.Parse(json);
Sort2(jObj);
var newJson = jObj.ToString();

void Sort2(System.Json.JsonObject jObj)
{
    var props = jObj.ToList();
    foreach (var prop in props)
    {
        jObj.Remove(prop.Key);
    }

    foreach (var prop in props.OrderByDescending(p => p.Key))
    {
        jObj.Add(prop);
        if (prop.Value is System.Json.JsonObject)
            Sort2((System.Json.JsonObject)prop.Value);
    }
}
Sign up to request clarification or add additional context in comments.

11 Comments

Interesting. But I'm using the built in System.Json in my app, so I'd rather use a solution that doesn't need a 3rd party lib.
@EduardoCoelho You can still use this solution with System.Json, just instead of deserializing with JSON.NET deserialize with System.Json, check here stackoverflow.com/questions/9573119/…
Thanks @I4V, I created a gist with your implementation + basic tests gist.github.com/4575133
Just wanted to say thank you for this. It helped me through a problem.
@I4V Yes. Property data is an array of JObjects. And these JObjects' properties don't get sorted because recursive call doesn't go inside JArray.
|
6

I know this may be a little late but, in case of you need to sort the internal arrays of data too (I just needed it):

static void Sort(JObject jObj)
{
    var props = jObj.Properties().ToList();
    foreach (var prop in props)
    {
        prop.Remove();
    }

    foreach (var prop in props.OrderBy(p => p.Name))
    {
        jObj.Add(prop);
        if (prop.Value is JObject)
            Sort((JObject)prop.Value);
        if (prop.Value is JArray)
        {
            Int32 iCount = prop.Value.Count();
            for (Int32 iIterator = 0; iIterator < iCount; iIterator++)
                if (prop.Value[iIterator] is JObject)
                    Sort((JObject)prop.Value[iIterator]);
        }
    }
}

Cheers!

Comments

3

By using this approach you can retrieve a dynamic object with your json data

At the DynamicJsonConverter create a SortedDictionary instead

var d = new SortedDictionary<string, object>(dictionary);
// TODO: code to sort inner objects
return new DynamicJsonObject(d);

Then you can use

string jsonStr = "{\"B\":\"2\",\"A\":\"1\"}";
JavaScriptSerializer jss = new JavaScriptSerializer();
jss.RegisterConverters(new JavaScriptConverter[] { new DynamicJsonConverter() });

dynamic json = jss.Deserialize(jsonStr, typeof(object)) as dynamic;

string result = new JavaScriptSerializer().Serialize((json as DynamicJsonObject).Dictionary);

And result will have the expected output.

1 Comment

Thanks @BrunoLM, however I ended up using the plain System.Json.JsonObject.
1

Using System.Text.Json (STJ):

using System.Text.Json;
using System.Text.Json.Nodes;

static JsonNode? SortKeys(JsonNode? node) =>
    node switch
    {
        JsonObject obj => new JsonObject(
            obj
                .Select(kv => KeyValuePair.Create(kv.Key, SortKeys(kv.Value)))
                .OrderBy(kv => kv.Key)
        ),
        JsonArray arr => new JsonArray(
            items: arr.Select(child => SortKeys(child)).ToArray()
        ),
        // This creates a copy of the node,
        // which we need to clear its parent node
        _ => node.Deserialize<JsonNode?>(),
    };

Given a JSON string, you can turn it into a JsonNode via JsonNode.Parse. And from a JsonNode back to a string via JsonNode.ToJsonString.

Comments

0

I wanted to sort contents of an array (not the array itself) and missed someone had done it above,

This ones functional, it clones the JSON

static JToken SortFunctional(JToken jt)
{
    if (jt is JObject)
    {
        var jo = (JObject)jt;
        var children =
            from p in jo.Properties()
            orderby p.Name
            select SortFunctional(p);
        return new JObject(children);
    }
    else if (jt is JArray)
    {
        var ja = (JArray)jt;
        var newElements =
            from e in ja
            select SortFunctional(e);
        return new JArray(newElements);
    }
    else if (jt is JProperty)
    {
        var jp = (JProperty)jt;
        return new JProperty(jp.Name,SortFunctional(jp.Value)); 
    }
    else
    {
        // presumably JValue
        return jt.DeepClone();
    }
}

I've used it for about 10 mins, it seems to work, but there maybe some weirdness in there.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.