C# 将带有数组的json结构扁平化为多个不带数组的扁平对象
我不确定我是否100%正确地描述了主题中的问题,但我相信这些例子会起到作用 我的JSON结构如下所示(注意:这可能会发生变化的可能性很小,所以我需要向前倾斜到通用解决方案) 一张包含多个行项目的发票: 这是所需的结果数据结构(具有重复数据和不同行项目信息的多张发票): 因此,“发票”中的每个“行项目”都应该“产生”一个新的发票,并带有重复的其他元素 结果数据结构的微小变化是可以接受的,我可以围绕它调整我的代码。 我一直在使用几个类似的问题,例如:C# 将带有数组的json结构扁平化为多个不带数组的扁平对象,c#,json,recursion,json.net,C#,Json,Recursion,Json.net,我不确定我是否100%正确地描述了主题中的问题,但我相信这些例子会起到作用 我的JSON结构如下所示(注意:这可能会发生变化的可能性很小,所以我需要向前倾斜到通用解决方案) 一张包含多个行项目的发票: 这是所需的结果数据结构(具有重复数据和不同行项目信息的多张发票): 因此,“发票”中的每个“行项目”都应该“产生”一个新的发票,并带有重复的其他元素 结果数据结构的微小变化是可以接受的,我可以围绕它调整我的代码。 我一直在使用几个类似的问题,例如: (等等)。我相信这是最接近的解决办法?但
- (等等)。我相信这是最接近的解决办法?但不确定是否有更好的方法
如有任何提示,我们将不胜感激。谢谢。您可以使用如下功能:
//Pass in the name of the array property you want to flatten
public string FlattenJson(string input, string arrayProperty)
{
//Convert it to a JObject
var unflattened = JsonConvert.DeserializeObject<JObject>(input);
//Return a new array of items made up of the inner properties
//of the array and the outer properties
var flattened = ((JArray)unflattened[arrayProperty])
.Select(item => new JObject(
unflattened.Properties().Where(p => p.Name != arrayProperty),
((JObject)item).Properties()));
//Convert it back to Json
return JsonConvert.SerializeObject(flattened);
}
var flattenedJson = FlattenJson(inputJson, "lineItems");
如果能够反序列化/序列化为强类型类,则可以使用自定义的
JsonConverter
。
我认为发票信息应该在某种半结构化对象中,因此这应该是可行的:
public class Invoice
{
public string ContactName { get; set; }
public List<Item> LineItems { get; set; } = new List<Item>();
public string InvoiceNumber { get; set; }
}
public class Item
{
public double Quantity { get; set; }
public string Description { get; set; }
}
要使用此转换器,请在序列化时提供它
var invoice = JsonConvert.DeserializeObject<Invoice>(inputJson);
var outputJson = JsonConvert.SerializeObject(invoice, new InvoiceFlattener());
var invoice=JsonConvert.DeserializeObject(inputJson);
var outputJson=JsonConvert.SerializeObject(invoice,new InvoiceFlatter());
正如您可能已经解决的那样,反序列化时此转换器不起作用,但是如果这是一项要求,您可以在ReadJson converter方法中编写逻辑。
缺点是,如果发票
类的结构发生变化,您将需要维护转换器。但它让我们置身于一个强类型的世界,有了外部库(一个开源库),您只需几行代码就可以将JSON转换为预期的CSV格式
string json = @"{
""contactName"": ""Company"",
""lineItems"": [
{
""quantity"": 7.0,
""description"": ""Beer No* 45.5 DIN KEG""
},
{
""quantity"": 2.0,
""description"": ""Beer Old 49.5 DIN KEG""
}
],
""invoiceNumber"": ""C6188372""
}";
StringBuilder sb = new StringBuilder();
using (var p = ChoJSONReader.LoadText(json))
{
using (var w = new ChoCSVWriter(sb)
.WithFirstLineHeader()
)
w.Write(p
.SelectMany(r1 => ((dynamic[])r1.lineItems).Select(r2 => new
{
r1.contactName,
r2.quantity,
r2.description,
r1.invoiceNumber
})));
}
Console.WriteLine(sb.ToString());
输出CSV:
contactName,quantity,description,invoiceNumber
Company,7,Beer No* 45.5 DIN KEG,C6188372
Company,2,Beer Old 49.5 DIN KEG,C6188372
希望能有所帮助。只是想澄清一下,您是否正在使用第一个JSON结构,并希望将其重新构造为第二个结构?或者您的C#类生成第一个JSON结构,而您希望它生成第二个JSON结构(实际上第一个JSON结构不应该存在)@Skintkingle First是正确的-我正在使用第一个JSON结构,我需要将其重新构造为第二个结构。目前是否有一个C#类可以很好地反序列化第一个示例?如果是,你能在问题中提供该类信息吗@Skintkingle由于动态数据结构,我正在处理object/JObject/JToken等,所以(遗憾的是)我没有固定的c#classes,所以您正在读取的JSON有一个未知的数据结构?或者它只是有条件地有时不在这里或那里提供参数?不幸的是,在这种情况下,我并不生活在强类型世界中-输入数据结构由外部Json模式“指定”,可以在任何给定时间修改,我需要适应它:(在这种情况下,我希望包含您的行项目列表的属性不会更改!DavidGs答案可能就是您想要的答案。我刚刚在他的答案下面写了一条评论:我将扩展他的答案,使其更加通用,(以检测数组),这样我就不需要硬编码“行项目”代码中的字符串。谢谢你的帮助。我已经尝试过了,我认为这会奏效。我需要将其扩展为递归,并检测哪些属性是数组,这样我就不需要显式地编写“lineItems”在代码中。无论如何,我已经接受了你的答案,因为它给了我如何继续的想法。谢谢。同时,我已经用DavidGs的答案做了我需要的,但是谢谢你提供的信息!我不知道这个库。我会查查看的。
var invoice = JsonConvert.DeserializeObject<Invoice>(inputJson);
var outputJson = JsonConvert.SerializeObject(invoice, new InvoiceFlattener());
string json = @"{
""contactName"": ""Company"",
""lineItems"": [
{
""quantity"": 7.0,
""description"": ""Beer No* 45.5 DIN KEG""
},
{
""quantity"": 2.0,
""description"": ""Beer Old 49.5 DIN KEG""
}
],
""invoiceNumber"": ""C6188372""
}";
StringBuilder sb = new StringBuilder();
using (var p = ChoJSONReader.LoadText(json))
{
using (var w = new ChoCSVWriter(sb)
.WithFirstLineHeader()
)
w.Write(p
.SelectMany(r1 => ((dynamic[])r1.lineItems).Select(r2 => new
{
r1.contactName,
r2.quantity,
r2.description,
r1.invoiceNumber
})));
}
Console.WriteLine(sb.ToString());
contactName,quantity,description,invoiceNumber
Company,7,Beer No* 45.5 DIN KEG,C6188372
Company,2,Beer Old 49.5 DIN KEG,C6188372