Arca Artem
Arca Artem

Reputation: 1083

How can I deserialize JSON data that have indexed references into strongly typed C# objects?

I want to deserialize JSON data (using Newtonsoft) similar to following, and convert to a strongly-typed object/list in C#, but can't figure out how to define the class such that indexed references are converted to the referenced objects.

{
  "Countries": [
    {
      "Name": "USA",

    },
    {
      "Name": "UK",
    },
    {
      "Name": "JAPAN",
    },
  ],
  "Authors": [
    {
      "DisplayName": "John Doe",
      "RealName": "Not John Doe"
    },
    {
      "DisplayName": "Jane Doe",
      "RealName": "Not Jane Doe"
    },
  ],
  "Books": [
    {
      "Author": 0,
      "Title": "A good read",
      "PublishedCountries": "0,1",

    },
    {
      "Author": 0,
      "Title": "Another good read",
      "PublishedCountries": "0,1",
    },
    {
      "Author": 1,
      "Title": "This one is even better",
      "PublishedCountries": "0,1,2",
    },
  ],
}

Ideally, I'd like to use classes similar to following:

public class Country
{
  public string Name { get; set;}
}

public class AuthorDetails
{
  public string DisplayName { get; set; }
  public string RealName { get; set; }
}

public class Book
{
  public AuthorDetails Author { get; set; }
  public string Title { get; set; }
  public IEnumerable<Country> PublishedCountries { get; set; }
}

public class ListOfBooks
{
  public IEnumerable<Book> Books { get; set; }
}

And deserialize like this:

var listOfBooks = JsonConvert.DeserializeObject<ListOfBooks>(jsonAsString);

I'm stuck as how to tell Json.Net that the Author property in the book JObject is an index, rather than an integer. Same goes for the PublishedCountries (that's comma-separated list of indexes)

Upvotes: 3

Views: 262

Answers (2)

L.B
L.B

Reputation: 116168

I can't see a way other than helping the deserialization process a little bit.

var dynObj = (JObject)JsonConvert.DeserializeObject(json);

var authors = dynObj["Authors"]
                .Select(j => new AuthorDetails { 
                            RealName = (string)j["RealName"], 
                            DisplayName = (string)j["DisplayName"] 
                })
                .ToList();

var countries = dynObj["Countries"]
                .Select(j => new Country { Name = (string)j["Name"]})
                .ToList();

var books = dynObj["Books"].Select(x => new Book
                {
                    Author = authors[(int)x["Author"]],
                    Title = (string)x["Title"],
                    PublishedCountries = x["PublishedCountries"].ToString().Split(',')
                                            .Select(i =>countries[int.Parse(i)])
                                            .ToList()
                })
                .ToList();

public class Country
{
    public string Name { get; set; }
}

public class AuthorDetails
{
    public string DisplayName { get; set; }
    public string RealName { get; set; }
}

public class Book
{
    public AuthorDetails Author { get; set; }
    public string Title { get; set; }
    public List<Country> PublishedCountries { get; set; }
}

Upvotes: 1

Adam
Adam

Reputation: 4227

I believe what you want to do is 'preserve object references'.

You can mark-up your C# object with attributes to describe when to utilise a reference, rather than duplicate the content of the object all over again:

[JsonObject(IsReference = true)] (use on the type declaration)

However, your generated Javascript will not look quite like what you have demonstrated, take a look here to see how to do it.

http://james.newtonking.com/projects/json/help/?topic=html/PreserveObjectReferences.htm

Instead, your JSON will look more like this:

{
  "Countries": [
    {
      "Name": "USA",

    },
    {
      "Name": "UK",
    },
    {
      "Name": "JAPAN",
    },
  ],
  "Authors": [
    {
      "DisplayName": "John Doe",
      "RealName": "Not John Doe"
    },
    {
      "DisplayName": "Jane Doe",
      "RealName": "Not Jane Doe"
    },
  ],
  "Books": [
    {
      "$id": 1,
      "Author": 0,
      "Title": "A good read",
      "PublishedCountries": "0,1",

    },
    {
      "$ref": 1
    },
    {
      "Author": 1,
      "Title": "This one is even better",
      "PublishedCountries": "0,1,2",
    },
  ],
}

Upvotes: 0

Related Questions