Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
234 views
in Technique[技术] by (71.8m points)

c# - How to deserialize a dynamically named root node with json.NET

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.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

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; }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...