Search code examples
c#jsonjson.net

Json.net: Merge two json arrays by objects Id


I would like to upset a json array by objects id, for example. let's say I have this json array:

 [{"Id":"1", "a":"1", "b":"2"},
  {"Id":"2", "a":"3", "b":"1"},
  {"Id":"3", "a":"5", "b":"1"}]

And I would like to Upsert it with this array

 [{"Id":"1", "a":"32", "b":"42"},
 {"Id":"2", "a":"3", "b":"1", "c":"23"},
  {"Id":"12", "a":"12", "b":"45"}]

The expected result should be:

[{"Id":"1", "a":"32", "b":"42"},
  {"Id":"2", "a":"3", "b":"1", "c":"23"},
  {"Id":"3", "a":"5", "b":"1"},
  {"Id":"12", "a":"12", "b":"45"}]

Solution

  • I think it can be easily done in C#. If you map your entities to something like that:

    [DataContract]
    public class Entity
    {
        [DataMember(Name = "Id")]
        public string Id { get; set; }
    
        [DataMember(Name = "a")]
        public int? A { get; set; }
    
        [DataMember(Name = "b")]
        public int? B { get; set; }
    
        [DataMember(Name = "c")]
        public int? C { get; set; }
    }
    

    I think it's not possible to perform desired action using LINQ, but good old foreach will solve your issue.

    EDIT: Actually after looking at @vadim-gremyachev answer I think it can be done with LINQ very well:

    var l1 = JsonConvert.DeserializeObject<IList<Entity>>(
        @"[{""Id"":""1"", ""a"":""1"", ""b"":""2""}, 
           {""Id"":""2"", ""a"":""3"", ""b"":""1""}, 
           {""Id"":""3"", ""a"":""5"", ""b"":""1""}]");
    
    var l2 = JsonConvert.DeserializeObject<IList<Entity>>(
        @"[{""Id"":""1"", ""a"":""32"", ""b"":""42""},
           {""Id"":""2"", ""a"":""3"", ""b"":""1"", ""c"":""23""},
           {""Id"":""12"", ""a"":""12"", ""b"":""45""}]");
    
    // LINQ
    var res = l1.Concat(l2).GroupBy(x => x.Id).Select(x => x.Last()).ToList();
    
    // Foraech
    var res2 = new List<Entity>(l1);
    foreach (var l2Entity in l2)
    {
        var resEntity = res2.FirstOrDefault(x => x.Id == l2Entity.Id);
        if (resEntity == null)
        {
            res2.Add(l2Entity);
        }
        else
        {
            res2[res2.IndexOf(resEntity)] = l2Entity;
        }
    }
    

    Then you can just serialize your res list back to JSON and it's done:

    var json = JsonConvert.SerializeObject(res);
    

    The resulting JSON will be:

    [
        {"Id":"1","a":32,"b":42},
        {"Id":"2","a":3,"b":1,"c":23},
        {"Id":"3","a":5,"b":1},
        {"Id":"12","a":12,"b":45}
    ]
    

    You can also use l1 and do not create res, it's depending on your case of course. You might also want to order the resulting collection by key after merge is done.