6

This is an example of the json file:

{
   "John Smith": {
      "id": "72389",
      "email": "[email protected]",
      "books": [
         {
            "id": "0",
            "title": "The Hunger Games",
            "rating": "5"
         },
         {
            "id": "1",
            "title": "Harry Potter and the Order of the Phoenix",
            "rating": "3"
         },
      ],
      "magazines": [
         {
            "id": "2",
            "title": "National Geographic",
            "rating": "1"
         },
         {
            "id": "3",
            "title": "Wired",
            "rating": "4"
         }
      ],
   }
}

Notice the root node has a dynamic name (John Smith), and every json I need to deserialize will have a different name. This json structure would require to have classes setup as follows:

public class RootObject
{
    public JohnSmith { get; set; }
}

public class JohnSmith //oops
{
    public string id { get; set; }
    public string email { get; set; }
    public List<Book> books { get; set; }
    public List<Magazine> magazines { get; set; }
}

public class Book
{
    public string id { get; set; }
    public string title { get; set; }
    public string rating { get; set; }
}

public class Magazine
{
    public string id { get; set; }
    public string title { get; set; }
    public string rating { get; set; }
}

My goal is to deserialize "bypassing/ignoring" root object and most importantly that dynamicaly named node. This is not crucial, but I would like to be able to get the last name and set as a property on the Person class.

public class Person
{
    public string id { get; set; }
    public string email { get; set; }
    public string name { get; set; }
    public List<Book> books { get; set; }
    public List<Magazine> magazines { get; set; }
}

public class Book
{
    public string id { get; set; }
    public string title { get; set; }
    public string rating { get; set; }
}

public class Magazine
{
    public string id { get; set; }
    public string title { get; set; }
    public string rating { get; set; }
}

Here is how I am doing this now:

var jo = JObject.Parse(json);
var deserializable = jo.First.First.ToString();

string name;
var jp = (JProperty)jo.First;
if (jp != null) name = jp.Name;

var person = JsonConvert.DeserializeObject<Person>(deserializable);
person.name = name;

This works OK, but I was wondering, maybe it could be done better by using a custom JsonConverter? I'm afraid this is a little bit over my head atm, so I am asking here for some help...

Anyway, if there is any better way to achieve this, please share.

2 Answers 2

13

I would keep the first part of your solution (deserializing to JObject), but I wouldn't do another serialization. My code would look like this:

var jo = JObject.Parse(json);
var jp = jo.Properties().First();
var name = jp.Name;
var person = jp.Value.ToObject<Person>();

Edit:

In case you want a custom converter, you could use the following code. The converter converts your object to a list of Persons where every property represents another Person.

class PersonListConverter : JsonConverter
{
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var list = (PersonList) value;

        writer.WriteStartObject();

        foreach (var p in list.Persons)
        {
            writer.WritePropertyName(p.Name);
            serializer.Serialize(writer, p);
        }

        writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var jo = serializer.Deserialize<JObject>(reader);
        var result = new PersonList();
        result.Persons = new List<Person>();

        foreach (var prop in jo.Properties())
        {
            var p = prop.Value.ToObject<Person>();
            // set name from property name
            p.Name = prop.Name;
            result.Persons.Add(p);
        }

        return result;
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(PersonList);
    }
}

Where PersonList would look like this:

[JsonConverter(typeof(PersonListConverter))]
class PersonList
{
    public List<Person> Persons { get; set; }
}
Sign up to request clarification or add additional context in comments.

4 Comments

how can I get the name of the main property ?
@RoyiNamir I'm not sure what you want to know. You want to get the name of which property exactly? cryodream's JSON example has only one person (= one property in the main object). My code iterates over all properties of the main object. But using the example JSON, there will only be one iteration, of course. So in my code there is no main property. All properties of the root object are treated the same (in the foreach loops).
I was referring to var jo = JObject.Parse(json); how can I get "john Smith" via jo ?
If you know the name you can access it by jo["John Smith"]. If you don't know the name, you have to iterate over all properties like in the foreach loop of the ReadJson method in my code. prop.Name is then "John Smith". You can read the docs about LINQ to JSON here: james.newtonking.com/json/help/?topic=html/LINQtoJSON.htm
0

I would try using a regex on the raw JSON first, extract the name, regex replace it with a fixed node name, then call deserialize on the modified result with the root node that you now know is there.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.