如何在 C# 中正确连接两个 Json 文件?
Posted
技术标签:
【中文标题】如何在 C# 中正确连接两个 Json 文件?【英文标题】:How to Right Join two Json files in C#? 【发布时间】:2021-08-18 19:28:24 【问题描述】:我想用一个公共键连接两个 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
]
有什么想法吗?
【问题讨论】:
将两个 json 转换为集合并加入? 【参考方案1】:你可以试试这个
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。
【讨论】:
1) 我从 Console.WriteLine(items) 得到这个输出;而且我太缺乏经验,无法理解。请参阅下一条评论。 System.Linq.Enumerable+WhereSelectEnumerableIterator2[f__AnonymousType12[f__AnonymousType02[ConsoleApp1.Program+Category,System.Collections.Generic.IEnumerable1[ConsoleApp1.Program+Item]],ConsoleApp1.Program+项目],f__AnonymousType2`4[System.String,System.String,System.String,System.String]] 2) 是的,我确信 SQL 中有一些错误,我主要是想传达所需连接的想法。 :)【参考方案2】:以下应该有效:
// 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
,其中包含来自JObject i
项目对象的所有记录,然后添加来自cat
类别对象的CategoryDescription
。它不会修改现有对象i
。
JContainer.Merge()
在这里不会为您提供帮助,因为它没有任何基于某些主键进行合并的能力。
ttObj.SelectTokens("Items[*]")
使用JSONPath wildcard operator [*]
选择"Items"
数组中的所有项目。
由于没有"CategoryID":"metals"
的类别,cat
在最终的select
语句中为空。
演示小提琴here.
【讨论】:
我创建了一个新问题,但也许从这里问会更容易。我需要过滤 (bool)objCat.["IncludeCategory"] == true。我尝试了很多方法,但都被 IntelliSense 拒绝或在运行时生成一个或 null 集。【参考方案3】:问题是您将“类别”列表与“项目”列表合并,而“项目”在 catObj
上不存在。
[我建议你转换类中的项目(使用visual studio你可以做一个“特殊粘贴”作为JSON类)。]
您必须遍历第一个列表的项目并与第二个列表中的相应元素合并,成员与成员,而不是列表与列表。
【讨论】:
我怀疑你是对的。我找到的例子都是合并的,我还没有足够的经验。感谢您的帮助。以上是关于如何在 C# 中正确连接两个 Json 文件?的主要内容,如果未能解决你的问题,请参考以下文章