Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Convert nested JSON to simple JSON

I'm trying to convert a nested json to simple json by recursively traversing. (Structure of input json is unknown)

for example, I want json like this

{
    "FirstName": "Rahul",
    "LastName": "B",
    "EmpType": {
        "RID": 2,
        "Title": "Full Time"
    },
    "CTC": "3.5",
    "Exp": "1",
    "ComplexObj": {
        "RID": 3,
        "Title": {
            "Test": "RID",
            "TWO": {
                "Test": 12
            }
        }
    }
}

to be converted something like this

{
    "FirstName": "Rahul",
    "LastName": "B",
    "EmpType__RID": 2,
    "EmpType__Title": "Full Time",
    "CTC": "3.5",
    "Exp": "1",
    "ComplexObj__RID": 3,
    "ComplexObj__Title__Test": "RID",
    "ComplexObj__Title__TWO__Test": 12
}

each fields in nested object will be changed to key which represents its actual path.

this is what I have done so far.

    public static void ConvertNestedJsonToSimpleJson(JObject jobject, ref JObject jobjectRef, string currentNodeName = "", string rootPath = "")
    {
        string propName = "";
        if (currentNodeName.Equals(rootPath))
        {
            propName = currentNodeName;
        }
        else
        {
            propName = (rootPath == "" && currentNodeName == "") ? rootPath + "" + currentNodeName : rootPath + "__" + currentNodeName;
        }

        foreach (JProperty jprop in jobject.Properties())
        {
            if (jprop.Children<JObject>().Count() == 0)
            {
                jobjectRef.Add(propName == "" ? jprop.Name : propName + "__" + jprop.Name, jprop.Value);
            }
            else
            {
                currentNodeName = jprop.Name;
                rootPath = rootPath == "" ? jprop.Name : rootPath;
                ConvertNestedJsonToSimpleJson(JObject.Parse(jprop.Value.ToString()), ref jobjectRef, currentNodeName, rootPath);
            }
        }
    }

and getting wrong result

{
    "FirstName": "Rahul",
    "LastName": "B",
    "EmpType__RID": 2,
    "EmpType__Title": "Full Time",
    "CTC": "3.5",
    "Exp": "1",
    "EmpType__ComplexObj__RID": 3,
    "EmpType__Title__Test": "RID",
    "EmpType__two__Test": 12
}

will appreciate any help on correcting my code, or any other approach to archive this.

like image 233
Rahul Bhosale Avatar asked Jan 17 '17 09:01

Rahul Bhosale


People also ask

How do I read nested JSON files?

Nested JSON is simply a JSON file with a fairly big portion of its values being other JSON objects. Compared with Simple JSON, Nested JSON provides higher clarity in that it decouples objects into different layers, making it easier to maintain. Using Phrase, keys will be stored by separating levels with a dot.

Does JSON support nested objects?

Objects can be nested inside other objects. Each nested object must have a unique access path. The same field name can occur in nested objects in the same document.

How does Jackson read nested JSON?

A JsonNode is Jackson's tree model for JSON and it can read JSON into a JsonNode instance and write a JsonNode out to JSON. To read JSON into a JsonNode with Jackson by creating ObjectMapper instance and call the readValue() method. We can access a field, array or nested object using the get() method of JsonNode class.


3 Answers

  • You don't need to convert the value of the property to string and then parse it again every time - just cast it to JObject
  • You don't need the complicated conditional logic to generate the name of the property - just use this: prefix + jprop.Name + "__"

The code:

public static void FlattenJson(JObject node, JObject result, string prefix = "")
{
    foreach (var jprop in node.Properties())
    {
        if (jprop.Children<JObject>().Count() == 0)
        {
            result.Add(prefix + jprop.Name, jprop.Value);
        }
        else
        {
            FlattenJson((JObject)jprop.Value, $"{prefix}{jprop.Name}__", result);
        }
    }
}

You can call it like this:

var node = JObject.Parse(/* the input string */);
var result = new JObject();
FlattenJson(node, result);
like image 107
Botond Balázs Avatar answered Nov 14 '22 23:11

Botond Balázs


Your problem is in the line rootPath = rootPath == "" ? jprop.Name : rootPath;. You are changing the rootPath when you first come across EmpType which means when you process ComplexObj your rootPath is wrong. What I believe you intended is just to change what you were passing into the recursive function.

As it is though it is unnecessary to keep track of root and currentnode as two separate items. Better would be to just track the current prefix for a given node in code that looks more like this:

public static void ConvertNestedJsonToSimpleJson(JObject input, JObject output, string prefix = "")
{
    foreach (JProperty jprop in input.Properties())
    {
        var name = prefix==""?jprop.Name:String.Format("{0}__{1}", prefix,jprop.Name);
        if (jprop.Children<JObject>().Count() == 0)
        {
            output.Add(name, jprop.Value);
        }
        else
        {
            ConvertNestedJsonToSimpleJson((JObject)jprop.Value, output, name);
        }
    }
}

This now gives me the output:

{
  "FirstName": "Rahul",
  "LastName": "B",
  "EmpType__RID": 2,
  "EmpType__Title": "Full Time",
  "CTC": "3.5",
  "Exp": "1",
  "ComplexObj__RID": 3,
  "ComplexObj__Title__Test": "RID",
  "ComplexObj__Title__TWO__Test": 12
}

which looks correct.

like image 20
Chris Avatar answered Nov 14 '22 23:11

Chris


Could you do something like this using linq

var jsonObj = jobject.select(x => new CustomJson {
   FirstName = x.FirstName,
   LastName = x.LastName,
   EmpTypeId = x.EmpType.Id,
   Title = x.EmpType.Title
   etc etc
});
like image 45
Bad Dub Avatar answered Nov 14 '22 21:11

Bad Dub