0

I have a JSON with the following structure:

    data": {
        "password": {
          "en": "Password",
          "ar": "Password",
          "zh": "Password",
          ... 
           },
          "confirmPassword": {
              "en": "Confirm password",
              "ar": "Confirm password",
           ...
          },
         "addressInputStrings": {
              "en": {
                  "search": "Address",
                  "placeholder": "Start typing your address",
                  "addressNotListed": "My address is not listed",
                  "country": "Country of residence",
                   "street": "Street",
                   ...
                  }
       "fileUploadStrings"
            {
           "en": 
               {
        "required": "Please upload a document.",
        "fileUploadSuccessful": "File uploaded successfully: ",
        "uploadError": {
         "multipartContentTypeError": "The request couldn't be processed (Error 1)",
         ...

Basically "data" is the root, and it has a number of properties, each has a list of language codes. The value is usually a string. Sometimes there is a second level of nesting, and sometimes even a third one.

I need to iterate through the JSON and get the following output

[key] (tab) [value of "en"]

if the value of "en" is not a string but another object, then

[key].[nested key (in "en")] (tab) [value]

if there is another level of nesting, then

[key].[nested key (in "en")].[nested nested key] (tab) [value]

So for the example the output would look like this:

password (tab) Passsword
confirmPassword (tab) Confirm password
addressInputStrings.search (tab) Address
addressInputStrings.addressInputStrings.placeholder (tab) Start typing your address
addressInputStrings.addressNotListed (tab) My address is not listed
addressInputStrings.country (tab) Country of residence
addressInputStrings.street (tab) Street
fileUploadStrings.required (tab) Please upload a document
fileUploadStrings.fileUploadSuccessful (tab) File uploaded successfully: 
fileUploadStrings.uploadError.multipartContentTypeErrorThe (tab) request couldn't be processed (Error 1) 

I thought this would be easy using Newtonsoft.Json, but it's like the library is actively fighting me. When I try to iterate children on a JObject I get Jproperties instead of JObjects, where I expect to get the first child, I get the whole collection again, I keep getting exceptions left and right, and let's not forget almost nothing wants to evaluate in the Watch window when I'm paused in debugger, so no experimentation is possible there. The only option is to modify, build, debug, get an error, repeat.

Could I get a quick and dirty solution? Thank you.

Shaggydog
  • 3,456
  • 7
  • 33
  • 50

1 Answers1

1

It sounds like the root of the problem here is that you are misunderstanding the design of Json.Net's LINQ-to-JSON API. A JObject can never directly contain another JObject. A JObject only ever contains JProperty objects. Each JProperty has a name and a value. The value of a JProperty in turn can be another JObject (or it can be a JArray or JValue). Please see this answer to JContainer, JObject, JToken and Linq confusion for more information on the relationships in the JToken hierarchy.

After understanding the hierarchy, the key to getting the output you want is the Descendants() extension method. This will allow you to do a recursive traversal with a simple loop. To get your output you are basically looking for the Path and Value for each leaf JProperty in the entire JSON. You can identify a leaf by checking whether the Value is a JValue.

So, putting it all together, here is how you would do it (I'm assuming C# here since you did not specify a language in your question):

var root = JObject.Parse(json);
var leafProperties = root.Descendants()
                         .OfType<JProperty>()
                         .Where(p => p.Value is JValue);

foreach (var prop in leafProperties)
{
    Console.WriteLine($"{prop.Path}\t{prop.Value}");
}

Fiddle: https://dotnetfiddle.net/l5Oqfb

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300