【问题标题】:Json.net: Merge two json arrays by objects IdJson.net:按对象 ID 合并两个 json 数组
【发布时间】:2021-07-16 12:41:05
【问题描述】:

例如,我想通过对象 id 来打乱 json 数组。假设我有这个 json 数组:

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

我想用这个数组来更新它

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

预期的结果应该是:

[{"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"}]

【问题讨论】:

  • MongoDB 在图中的位置是什么?
  • 而当我们这样做的时候,对象 2 的 c=23 属性是从哪里来的?
  • 感谢 lc,我已经解决了这个问题。
  • H W,谢谢,但这并不能回答我的问题,它是相似的,但我需要能够 upsert 而不是更新或插入

标签: c# json json.net


【解决方案1】:

我认为它可以在 C# 中轻松完成。如果您将实体映射到类似的东西:

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

我认为使用 LINQ 无法执行所需的操作,但好的旧 foreach 将解决您的问题。

编辑:实际上在查看@vadim-gremyachev 的答案后,我认为使用 LINQ 可以很好地完成:

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

然后您可以将您的 res 列表序列化回 JSON 并完成:

var json = JsonConvert.SerializeObject(res);

生成的 JSON 将是:

[
    {"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}
]

您也可以使用l1 而不要创建res,这当然取决于您的情况。您可能还想在合并完成后按键对生成的集合进行排序。

【讨论】:

  • 您实际上并不需要一个实体来执行所要求的操作,但这可能是一个好主意,因为我猜 OP 可能想要对实体进行其他操作,所以很高兴有一个强类型的对象。但真正没用的是DataContractDataMember 属性。您可以使用具有一些小写属性的实体来生存。
  • 在提供的 JSON 中有一个奇怪的命名,当Id 以大写字母开头而其他属性不是。在这种情况下,如果您希望生成的 JSON 遵循相同的规则,Data 属性可能是一个不错的选择。
【解决方案2】:

您可以在 JArray 上简单地使用 Linq,因为它们是 IEnumerable&lt;JToken&gt;

var first = JArray.Parse(@"[{'Id':'1', 'a':'1', 'b':'2'},
            {'Id':'2', 'a':'3', 'b':'1'},
            {'Id':'3', 'a':'5', 'b':'1'}]");


var second = JArray.Parse(@"[{'Id':'1', 'a':'32', 'b':'42'},
            {'Id':'2', 'a':'3', 'b':'1', 'c':'23'},
            {'Id':'12', 'a':'12', 'b':'45'}]");

var resultAsEnumerable = first.Concat(second)
                              .GroupBy(t => t["Id"])
                              .Select(g => g.Last());

如果您需要 JArray 形式的结果,您可以轻松地将结果转换为它:

var resultAsJArray = new JArray(resultAsEnumerable.ToArray());

【讨论】:

    【解决方案3】:
    var x = JArray.Parse(@"[{'Id':'1', 'a':'1', 'b':'2'},
                            {'Id':'2', 'a':'3', 'b':'1'},
                            {'Id':'3', 'a':'5', 'b':'1'}]");
    
    
    var y = JArray.Parse(@"[{'Id':'1', 'a':'32', 'b':'42'},
                            {'Id':'2', 'a':'3', 'b':'1', 'c':'23'},
                            {'Id':'12', 'a':'12', 'b':'45'}]");
    
    //1. Union arrays skipping items that already exist
    x.Merge(y, new JsonMergeSettings
            {
                MergeArrayHandling = MergeArrayHandling.Union,
            });
    
    //2. Get distinct items by key (Id) 
    var result = x.GroupBy(i => i["Id"]).Select(g => g.Last()).ToList();
    

    【讨论】:

      【解决方案4】:

      我尝试了其中一些答案,但它们只对我部分有用,订购结果 json 留给读者练习,您可能希望输入 json 没有 ID 作为字符串才能正常工作。使用 .net core 3.1 测试。

      var input1 = JsonConvert.DeserializeObject<JArray>(@"[  {'Id':'1', 'a':'1', 'b':'2'},
                                                              { 'Id':'2', 'a':'3', 'b':'1'},
                                                              { 'Id':'3', 'a':'5', 'b':'1'}]");
      
      var input2 = JsonConvert.DeserializeObject<JArray>(@" [  {'Id':'1', 'a':'32', 'b':'42'},
                                                               {'Id':'2', 'a':'3', 'b':'1', 'c':'23'},
                                                               {'Id':'12', 'a':'12', 'b':'45'}]");
      
      //you may want this the other way depending on what your trying to do
      //input1.Merge(input2);
      input2.Merge(input1);
      
      var res = new List<dynamic>();
      
      foreach (var x in input2.GroupBy(x => x["Id"]).ToList())
      {
            var newItem = new ExpandoObject();
            foreach (var y in x)
            {
                  foreach (JProperty z in y)
                  {
                       newItem.TryAdd(z.Name, z.Value);
                  }
             }
             res.Add(newItem);
      
       }
      
       Console.WriteLine(JsonConvert.SerializeObject(res));
       Console.ReadLine();
      

      输出:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-12-01
        • 2016-03-22
        • 1970-01-01
        • 2018-01-14
        • 2021-11-19
        • 2021-03-08
        • 1970-01-01
        相关资源
        最近更新 更多