Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Deserialize two slightly different JSON strings (same structure, different names) to the same class

My problem in short: I need to deserialize two big JSON strings to one class, but strings are little different. Here is the first one:

 {  
  "persons": [  
    {  
      "age":30,
      "name":"david",
      "hobbies": [  
        {  
          "name":"tennis",
          "hours":5
        },
       {  
         "name":"football",
         "hours":10
       }
     ]
   },
   {  
     "name":"adam",
     "age":23,
     "hobbies":[]
   }
 ]   
}  

and the other one:

{  
  "person": [  
    {  
      "age":25,
      "name":"dave",
      "hobbies":[  
        {  
          "name":"Basketball",
          "hours":5
        },
        {  
          "name":"football",
          "hours":10
        }
      ]
    },
    {  
      "name":"Steve",
      "age":28,
      "hobbies": []
    }
  ]
}

You can see that one time it's "Persons", and the other time it's "Person". Is there any simple solution to this? I was thinking about creating two lists in my class

List<Person> person;
List<Person> persons;

and after deserialization combine then manually somehow. But there must be a simplier way.

By the way, this is not the exact code I need to deserialize. I just wanted to keep the main idea as simple as possible.

like image 787
Jonas Avatar asked Oct 19 '22 10:10

Jonas


1 Answers

One simple solution is to use a single list in your class, but then add an alternate setter with the other name which references the same list. You can even make it private as long as you decorate it with a [JsonProperty] attribute. That way the public interface of your class will look normal, but it will still work with both JSON property names.

public class RootObject
{
    [JsonProperty("persons")]
    public List<Person> People { get; set; }

    // This is visible to Json.Net and references the real list
    [JsonProperty("person")]
    private List<Person> Person
    {
        set { People = value; }
    }
}

public class Person
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("age")]
    public int Age { get; set; }

    [JsonProperty("hobbies")]
    public List<Hobby> Hobbies { get; set; }
}

public class Hobby
{
    [JsonProperty("name")]
    public string Name { get; set; }

    [JsonProperty("hours")]
    public int Hours { get; set; }

}

Fiddle: https://dotnetfiddle.net/9Dw48J

EDIT

To address your comments: PopulateObject can be used to blindly augment an existing object with data from JSON, but it will not do the kind of merging you are looking for. It seems you are looking for a way to match people by name (and maybe age?) and then combine the hobbies for those people. For that you will need to write your own logic. I would suggest deserializing each list into separate instances of your RootObject class, then merging the data in a post-processing step. You can make a method on the RootObject class which will accept another RootObject to merge. Perhaps something like this:

public class RootObject
{
    ...

    public void MergeWith(RootObject other)
    {
        if (other.People == null) return;
        if (People == null) People = new List<Person>();
        foreach (Person person in other.People)
        {
            // You may need to make changes here--
            // How do you determine whether two people are the same?
            Person existingPerson = People.FirstOrDefault(p => p.Name == person.Name && 
                                                               p.Age == person.Age);
            if (existingPerson != null)
            {
                existingPerson.MergeWith(person);
            }
            else
            {
                People.Add(person);
            }
        }
    }
}

And on the Person class...

public class Person
{
    ...

    public void MergeWith(Person other)
    {
        if (other.Hobbies == null) return;
        if (Hobbies == null) Hobbies = new List<Hobby>();
        foreach (Hobby hobby in other.Hobbies)
        {
            Hobby existingHobby = Hobbies.FirstOrDefault(h => h.Name == hobby.Name);
            if (existingHobby != null)
            {
                // You may need to make changes here--
                // What do you do if two hobbies have the same name but different hours?
                existingHobby.Hours += hobby.Hours;
            }
            else
            {
                Hobbies.Add(hobby);
            }
        }
    }
}

To deserialize and merge you would then do:

var firstObj = JsonConvert.DeserializeObject<RootObject>(firstJson);
var secondObj = JsonConvert.DeserializeObject<RootObject>(secondJson);
firstObj.MergeWith(secondObj);

Fiddle: https://dotnetfiddle.net/8Fiwsd

like image 86
Brian Rogers Avatar answered Oct 21 '22 01:10

Brian Rogers