我不确定我是否正确地以100%的方式描述了问题,但我相信这些示例可以解决问题。
我具有如下所示的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"
}
这是所需的结果数据结构(具有相同数据和不同订单项信息的多张发票):
[{
"contactName": "Company",
"quantity": 7.0,
"description": "Beer No* 45.5 DIN KEG"
"invoiceNumber": "C6188372"
},{
"contactName": "Company",
"quantity": 2.0,
"description": "Beer Old 49.5 DIN KEG"
"invoiceNumber": "C6188372"
}]
因此,“发票”中的每个“订单项”都应“生成”具有重复的其他元素的新发票。
可接受围绕结果数据结构的小变化,我可以围绕它调整代码。 我一直在使用一些类似的问题,例如:
有关更多背景信息,我需要此文件用于CSV导出。因此,结果集应为生成的CSV中的两行。
任何提示/技巧都非常感谢。谢谢。
答案 0 :(得分:4)
您可以使用以下函数来做到这一点:
//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");
答案 1 :(得分:2)
使用外部库Cinchoo ETL-一个开放源代码库,只需几行代码即可将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
希望有帮助。
答案 2 :(得分:1)
如果您能够反序列化/序列化为强类型类,则可以使用自定义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; }
}
然后使用JsonConverter可以根据Items(或您可能需要的任何其他属性)将其展平
public class InvoiceFlattener : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var obj = value as Invoice;
if (obj == null)
{
return;
}
writer.WriteStartArray();
foreach (var item in obj.LineItems)
{
writer.WriteStartObject();
writer.WritePropertyName(nameof(obj.ContactName));
writer.WriteValue(obj.ContactName);
writer.WritePropertyName(nameof(item.Quantity));
writer.WriteValue(item.Quantity);
writer.WritePropertyName(nameof(item.Description));
writer.WriteValue(item.Description);
writer.WritePropertyName(nameof(obj.InvoiceNumber));
writer.WriteValue(obj.InvoiceNumber);
writer.WriteEndObject();
}
writer.WriteEndArray();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Invoice);
}
}
要使用此Converter,请在序列化时提供它
var invoice = JsonConvert.DeserializeObject<Invoice>(inputJson);
var outputJson = JsonConvert.SerializeObject(invoice, new InvoiceFlattener());
您可能已经知道,在反序列化时此转换器不起作用,但是如果有此要求,则可以在ReadJson转换器方法中编写逻辑。
不利的一面是,如果Invoice
类的结构发生变化,您将需要维护转换器。但这使我们处于一个强类型的世界