对象分离键值对集合
Object to separate KeyValue Pair collections
我遇到了一个挑战,我需要将任何对象展平成键值对格式
这对于简单的 类 甚至 类 都非常有效,其中我还有其他 类
看一个例子,
public class Buyer
{
[JsonProperty("name")]public string Name { get; set; }
[JsonProperty("address")]public string Address { get; set; }
[JsonProperty("lastPurchase")]public Purchase LastPurchase { get; set; }
public Buyer()
{
Name = "Joe Bloggs";
Address = "An adddress somewhere";
AllPurchases = new List<Purchase>()
{
new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2017-01-01")},
new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")}
};
LastPurchase = new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")};
}
[JsonIgnore]
public List<Purchase> AllPurchases { get; set; }
}
public class Purchase
{
public DateTime PurchaseDateTime { get; set; }
public double PurchaseAmount { get; set; }
}
我有下面的代码,这是我当前的实现
var buyer = new Buyer();
var json = JsonConvert.SerializeObject(buyer);
var obj = JObject.Parse(json);
var result = obj.Descendants()
.OfType<JProperty>()
.Where(s => s.Value.Type != JTokenType.Object)
.Select(p => new KeyValuePair<string, string>(p.Path,
p.Value.Type == JTokenType.Array || p.Value.Type == JTokenType.Object
? null : p.Value.ToString()));
var serializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var newJson = JsonConvert.SerializeObject(result, serializerSettings);
Console.WriteLine(newJson);
这生成下面的 Json 是完美的
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
}
]
当我通过删除 JsonIgnore
引入序列化列表时,事情变得棘手
现在我明白了
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
},
{
"key": "allPurchases",
"value": null
},
{
"key": "allPurchases[0].PurchaseDateTime",
"value": "01/01/2017 00:00:00"
},
{
"key": "allPurchases[0].PurchaseAmount",
"value": "100"
},
{
"key": "allPurchases[1].PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "allPurchases[1].PurchaseAmount",
"value": "100"
}
]
这显然发生了,因为我的逻辑中没有任何用于处理列表的特定内容
我怎样才能改变我的逻辑,使 AllPurchases 是一个键值对集合,键是 allPurchases[0],allPurchases[1],值是单独的键值集合,这将避免像 allPurchases[ 这样的键名0].PurchaseAmount 等?
我需要保持解决方案的通用性,以便它将任何对象展平到这个结构中
保罗
据我所知,您想要以下内容。
不在数组中的值和对象以 {key:"propertyPath", value:"valueTostring"} 的形式传输到 json 个对象
子对象到键值对数组。
要索引的数组 {key:"property[index]", value:"valueTostringOrObjectKeyValueArray"}
以下
var result = GetItmes(obj);
IEnumerable<KeyValuePair<string,object>> GetItmes(in JToken token, string path = "")
{
return token switch
{
JObject jObject => from prop in token.Children<JProperty>()
from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
select child,
JArray jArray => from item in jArray.Select((t, i) => (t, i))
select new KeyValuePair<string, object>($"{path}[{item.i}]",GetItmes(item.t)),
JValue jValue => new[] {
new KeyValuePair<string, object>(path, (object)jValue?.ToString())
},
_ => Enumerable.Empty<KeyValuePair<string, object>>(),
};
}
将创建
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "1/1/2018 12:00:00 AM"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
},
{
"key": "AllPurchases[0]",
"value": [
{
"key": "PurchaseDateTime",
"value": "1/1/2017 12:00:00 AM"
},
{
"key": "PurchaseAmount",
"value": "100"
}
]
},
{
"key": "AllPurchases[1]",
"value": [
{
"key": "PurchaseDateTime",
"value": "1/1/2018 12:00:00 AM"
},
{
"key": "PurchaseAmount",
"value": "100"
}
]
}
]
此代码是递归的且未优化我相信可以有更有效的方法来执行此操作。
为了
IEnumerable<KeyValuePair<string, object>> GetItmes(JToken token, string path = "")
{
switch (token)
{
case JObject jObject:
return from prop in token.Children<JProperty>()
from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
select child;
case JArray jArray:
return from item in jArray.Select((t, i) => (t, i))
select new KeyValuePair<string, object>($"{path}[{item.i}]", GetItmes(item.t));
case JValue jValue:
return new[] {
new KeyValuePair<string, object>(path, (object)jValue?.ToString())
};
default: return Enumerable.Empty<KeyValuePair<string, object>>();
};
}
我遇到了一个挑战,我需要将任何对象展平成键值对格式
这对于简单的 类 甚至 类 都非常有效,其中我还有其他 类
看一个例子,
public class Buyer
{
[JsonProperty("name")]public string Name { get; set; }
[JsonProperty("address")]public string Address { get; set; }
[JsonProperty("lastPurchase")]public Purchase LastPurchase { get; set; }
public Buyer()
{
Name = "Joe Bloggs";
Address = "An adddress somewhere";
AllPurchases = new List<Purchase>()
{
new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2017-01-01")},
new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")}
};
LastPurchase = new Purchase() {PurchaseAmount = 100, PurchaseDateTime = Convert.ToDateTime("2018-01-01")};
}
[JsonIgnore]
public List<Purchase> AllPurchases { get; set; }
}
public class Purchase
{
public DateTime PurchaseDateTime { get; set; }
public double PurchaseAmount { get; set; }
}
我有下面的代码,这是我当前的实现
var buyer = new Buyer();
var json = JsonConvert.SerializeObject(buyer);
var obj = JObject.Parse(json);
var result = obj.Descendants()
.OfType<JProperty>()
.Where(s => s.Value.Type != JTokenType.Object)
.Select(p => new KeyValuePair<string, string>(p.Path,
p.Value.Type == JTokenType.Array || p.Value.Type == JTokenType.Object
? null : p.Value.ToString()));
var serializerSettings = new JsonSerializerSettings
{
Formatting = Formatting.Indented,
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var newJson = JsonConvert.SerializeObject(result, serializerSettings);
Console.WriteLine(newJson);
这生成下面的 Json 是完美的
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
}
]
当我通过删除 JsonIgnore
引入序列化列表时,事情变得棘手现在我明白了
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
},
{
"key": "allPurchases",
"value": null
},
{
"key": "allPurchases[0].PurchaseDateTime",
"value": "01/01/2017 00:00:00"
},
{
"key": "allPurchases[0].PurchaseAmount",
"value": "100"
},
{
"key": "allPurchases[1].PurchaseDateTime",
"value": "01/01/2018 00:00:00"
},
{
"key": "allPurchases[1].PurchaseAmount",
"value": "100"
}
]
这显然发生了,因为我的逻辑中没有任何用于处理列表的特定内容
我怎样才能改变我的逻辑,使 AllPurchases 是一个键值对集合,键是 allPurchases[0],allPurchases[1],值是单独的键值集合,这将避免像 allPurchases[ 这样的键名0].PurchaseAmount 等?
我需要保持解决方案的通用性,以便它将任何对象展平到这个结构中
保罗
据我所知,您想要以下内容。 不在数组中的值和对象以 {key:"propertyPath", value:"valueTostring"} 的形式传输到 json 个对象 子对象到键值对数组。 要索引的数组 {key:"property[index]", value:"valueTostringOrObjectKeyValueArray"} 以下
var result = GetItmes(obj);
IEnumerable<KeyValuePair<string,object>> GetItmes(in JToken token, string path = "")
{
return token switch
{
JObject jObject => from prop in token.Children<JProperty>()
from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
select child,
JArray jArray => from item in jArray.Select((t, i) => (t, i))
select new KeyValuePair<string, object>($"{path}[{item.i}]",GetItmes(item.t)),
JValue jValue => new[] {
new KeyValuePair<string, object>(path, (object)jValue?.ToString())
},
_ => Enumerable.Empty<KeyValuePair<string, object>>(),
};
}
将创建
[
{
"key": "name",
"value": "Joe Bloggs"
},
{
"key": "address",
"value": "An adddress somewhere"
},
{
"key": "lastPurchase.PurchaseDateTime",
"value": "1/1/2018 12:00:00 AM"
},
{
"key": "lastPurchase.PurchaseAmount",
"value": "100"
},
{
"key": "AllPurchases[0]",
"value": [
{
"key": "PurchaseDateTime",
"value": "1/1/2017 12:00:00 AM"
},
{
"key": "PurchaseAmount",
"value": "100"
}
]
},
{
"key": "AllPurchases[1]",
"value": [
{
"key": "PurchaseDateTime",
"value": "1/1/2018 12:00:00 AM"
},
{
"key": "PurchaseAmount",
"value": "100"
}
]
}
]
此代码是递归的且未优化我相信可以有更有效的方法来执行此操作。
为了
IEnumerable<KeyValuePair<string, object>> GetItmes(JToken token, string path = "")
{
switch (token)
{
case JObject jObject:
return from prop in token.Children<JProperty>()
from child in GetItmes(prop.Value, string.IsNullOrEmpty(path) ? prop.Name : $"{path}.{prop.Name}")
select child;
case JArray jArray:
return from item in jArray.Select((t, i) => (t, i))
select new KeyValuePair<string, object>($"{path}[{item.i}]", GetItmes(item.t));
case JValue jValue:
return new[] {
new KeyValuePair<string, object>(path, (object)jValue?.ToString())
};
default: return Enumerable.Empty<KeyValuePair<string, object>>();
};
}