如何在 C# 中右连接两个 Json 文件?

How to Right Join two Json files in C#?

我想使用公用键连接两个 json 文件,并从右侧文件中获取所有记录并从左侧文件中获取匹配数据。

如果是SQL。

SELECT json1.CategoryDescription, json2.CategoryID, json2.TechName, json2.SpawnID
FROM json1
RIGHT JOIN json2
ON json1.CategoryID = json2.CategoryID
WHERE GameVersion = "A" OR GameVersoion = "2" AND CategoryID = "metals"

我需要获取所有 json2 条记录和每个记录的 json1.CategoryDescription。但目前它只列出 json1 中的所有记录,然后列出 json2.

中的所有记录

这是我目前的尝试:

using System;
using System.IO;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;

namespace ConsoleApp1
{
    public class Program
    {
        public static void Main()
        {
            // Filter variables
            var gameVer = "2";
            var cat = "metals";

            // Load the categories.json
            JObject catObj = JObject.Load(new JsonTextReader(File.OpenText("D:/Code/Tests/categories.json")));
            // Load the techtype.json
            JObject ttObj = JObject.Load(new JsonTextReader(File.OpenText("D:/Code/Tests/techtypes.json")));
            // Read techtype.json into an array

            var mergeSettings = new JsonMergeSettings
            {
                MergeArrayHandling = MergeArrayHandling.Union
            };
            catObj.Merge(ttObj, mergeSettings);

            // Does not work,
            /*
             Unhandled exception. System.NullReferenceException: Object reference not set to an instance of an object.
            at ConsoleApp1.Program.Main() in D:\Code\Tests\ReadTechTypes\ReadTechTypes\Program.cs:line 30
            */
            // (catObj.SelectToken("Categoris") as JArray).Merge(ttObj.SelectToken("TechType"), mergeSettings);

            // Does not work, same error
            //var mergedArray = catObj.SelectToken("Categoris") as JArray;
            //string json = mergedArray.ToString();

            Console.WriteLine(catObj);
        }
    }
}

左边json

{
   "Categories":[
      {
         "CategoryID":"baseupgrades",
         "CategoryDescription":"Base Upgrades",
         "IncludeCategory":true,
         "GameVersion":"A"
      },
      {
         "CategoryID":"batteries",
         "CategoryDescription":"Batteries",
         "IncludeCategory":true,
         "GameVersion":"A"
      },
      {
         "CategoryID":"blueprint",
         "CategoryDescription":"Blueprint",
         "IncludeCategory":false,
         "GameVersion":"A"
      }
      // Other category values omitted
   ]
}

右边json

{
   "Items":[
      {
         "CategoryID":"crystalline",
         "TechName":"Quartz",
         "SpawnID":"quartz",
         "TechID":1,
         "GameVersion":"A"
      },
      {
         "CategoryID":"metals",
         "TechName":"Metal Salvage",
         "SpawnID":"scrapmetal",
         "TechID":2,
         "GameVersion":"A"
      },
      {
         "CategoryID":"outcrop",
         "TechName":"Limestone Outcrop",
         "SpawnID":"limestonechunk",
         "TechID":4,
         "GameVersion":"A"
      }
      // Other items omitted
   ]
}

有什么想法吗?

问题是您正在将“类别”列表与“项目”列表合并,而“项目”不在 catObj 上。

[我建议你转换 class 中的项目(使用 visual studio 你可以做一个“特殊粘贴”,如 JSON class)。]

您必须遍历第一个列表的项目并与第二个列表中的相应元素合并,成员与成员,而不是列表与列表。

你可以试试这个

categoriesRoot = JsonConvert.DeserializeObject<CategoriesRoot>(categoriesJson);
itemsRoot = JsonConvert.DeserializeObject<ItemsRoot>(itemsJson);


var items = from cr in categoriesRoot.Categories
    join ir in itemsRoot.Items on cr.CategoryID equals ir.CategoryID into irj
    from ir in irj.DefaultIfEmpty()
    where ( (cr.GameVersion == "A") || (cr.GameVersion == "2" && cr.CategoryID == "metals"))
                 select new { 
                  cr.CategoryDescription,
                  ir.CategoryID,
                  ir.TechName,
                  ir.SpawnID
    };

var newItemsJson=JsonConvert.SerializeObject(items);

创建这些后 类

public class Item
{
    public string CategoryID { get; set; }
    public string TechName { get; set; }
    public string SpawnID { get; set; }
    public int TechID { get; set; }
    public string GameVersion { get; set; }
}

public class ItemsRoot
{
    public List<Item> Items { get; set; }
}


public class Category
{
    public string CategoryID { get; set; }
    public string CategoryDescription { get; set; }
    public bool IncludeCategory { get; set; }
    public string GameVersion { get; set; }
}

public class CategoriesRoot
{
    public List<Category> Categories { get; set; }
}

输出会像这样

[
{"CategoryDescription":"Base Upgrades","CategoryID":"crystalline","TechName":"Quartz","SpawnID":"quartz"},
{"CategoryDescription":"Batteries","CategoryID":"metals","TechName":"Metal Salvage","SpawnID":"scrapmetal"}
]

顺便说一下,您的 SQL 查询中有一个错误

WHERE GameVersion = "A" OR GameVersoion = "2" AND CategoryID = "metals"

这是一个有歧义的代码,因为两个查询中都有 GameVersion 和 CategoryID。

以下应该有效:

// Filter variables
var gameVersions = new HashSet<string> { "A", "2" };
var categoryIDs = new HashSet<string> { "metals" };

// Left outer join on ttObj.  Select all Items[*] array items
var query = from i in ttObj.SelectTokens("Items[*]").OfType<JObject>()
    // Filter on the game version and category ID
    let categoryId = (string)i["CategoryID"]           
    let gameVersion = (string)i["GameVersion"]
    where categoryIDs.Contains(categoryId) && gameVersions.Contains(gameVersion)
    // Join with "Categories[*]" on category ID
    join c in catObj.SelectTokens("Categories[*]") on categoryId equals (string)c["CategoryID"] into joined
    // DefaultIfEmpty makes this a left join
    from cat in joined.DefaultIfEmpty()               
    // Select all records of i and add the CategoryDescription from cat.
    select new JObject(i.Properties()) { new JProperty("CategoryDescription", cat?["CategoryDescription"]) };

var results = query.ToList(); // Materialize the query into a list of results.

这导致:

[
  {
    "CategoryID": "metals",
    "TechName": "Metal Salvage",
    "SpawnID": "scrapmetal",
    "TechID": 2,
    "GameVersion": "A",
    "CategoryDescription": null
  }
]

备注:

  • 我将查询从右连接更改为左连接,因为它使过滤看起来更自然一些。如果您更喜欢正确的连接语法,请参阅 LINQ Left Join And Right Join

  • 最后的 select 语句使用 JObject i 项目对象中的所有记录创建一个新的 JObject,然后从 cat类对象。它不修改现有对象 i.

  • JContainer.Merge() 不会在这里帮助你,因为它没有任何基于某些主键的合并能力。

  • ttObj.SelectTokens("Items[*]") uses the JSONPath wildcard operator [*] 到 select "Items" 数组中的所有项目。

  • 由于没有带"CategoryID":"metals"的类别,cat在最终的select语句中为null。

演示 fiddle here.