实施JsonConverter后,嵌套节点的格式不正确 [英] Nested nodes are not formatted correctly after implementing JsonConverter
问题描述
我有一个要序列化为JSON的对象列表,如下所示,因为此列表中存在复杂的类型.我想将此复杂类型更改为Key/Value
对,其中每个Key
是该类型中属性的名称,每个Value
是该属性的对应值.我尝试了多种解决方案,但没有一个对我有用.
I have a list of objects that I want to serialize as JSON like below, since there is a complex type in this list. I want to change this complex type to Key/Value
pairs where each Key
is the name of a property in the type, and each Value
is the corresponding value of that property. I've tried multiple solutions but none of them worked for me.
这是对象结构
public class Metadata
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
public class Data
{
// If I change the type of Metadata to IList<IDictionary<string, object>>
// custom converter won't catch it at all when I pass it to its constructor
//public IList<IDictionary<string, object>> Metadata { get; set; }
public IList<Metadata> Metadata { get; set; }
public int Length { get; set; }
public string Type { get; set; }
}
这是我想要的输出,在IList<Metadata>
Here is my desired output with two entries in IList<Metadata>
{
"Metadata": [{
"Key": "FirstName",
"Value": "ABC"
},
{
"Key": "LastName",
"Value": "XYZ"
},
{
"Key": "FirstName",
"Value": "DEF"
},
{
"Key": "LastName",
"Value": "MNL"
}
],
"Length": 25,
"Type": "application/mp3"
}
我知道JsonSerializer
本身不会更改对象的外观,因此我尝试通过实现自定义JsonConverter
来更改它:
I know that JsonSerializer
does not change the face of the object by itself, so I tried to change it by implementing a custom JsonConverter
:
public class KeyValue
{
public string Key { get; set; }
public string Value { get; set; }
}
class CustomMetadataConverter : JsonConverter
{
private readonly Type[] _types;
public CustomMetadataConverter(params Type[] types)
{
_types = types;
}
public override bool CanConvert(Type objectType)
{
return _types.Any(t => t == objectType);
}
// I've removed ReadJson and CanRead here to keep the question clear
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken token = JToken.FromObject(value);
if (token.Type != JTokenType.Object)
token.WriteTo(writer);
else
{
JObject jsonObject = (JObject)token;
IList<KeyValue> properties = jsonObject.Properties()
.Select(p => new KeyValue { Key = p.Name, Value = p.Value.ToString() }).ToList();
// If I change the IList<KeyValue> to anonymous array, output would be the same
//var properties = jsonObject.Properties().Select(p => new { Key = p.Name, Value = p.Value.ToString() }).ToArray();
jsonObject.RemoveAll();
jsonObject.Add(new JProperty("Metadata", JToken.FromObject(properties)));
jsonObject.WriteTo(writer);
}
}
}
这就是我的称呼:
var serializedObject = JsonConvert.SerializeObject(listOfData, Formatting.Indented, new CustomMetadataConverter(typeof(Metadata)));
我尝试了此解决方案,将其移至根目录,但是自定义转换器的输出被父文件包装更换它.我知道这是因为自定义转换器仅读取Metadata
的子级,但是如果将CustomMetadataConverter(typeof(Metadata))
更改为CustomMetadataConverter(typeof(Data))
,它将把整个查询转换为Key/Value
对.那不是我想要的.
I tried this solution to move it to root, but the output of the custom converter is wrapped with the parent instead of replacing it. I know it's because the custom converter only reads children of Metadata
but if I change CustomMetadataConverter(typeof(Metadata))
to CustomMetadataConverter(typeof(Data))
it converts the whole query to Key/Value
pairs. And that's not what I want.
这是实现自定义转换器后的输出
{
"Metadata": [
{
"Metadata": [
{
"Key": "FirstName",
"Value": "ABC"
},
{
"Key": "LastName",
"Value": "XYZ"
}
]
}
],
"Length": 25,
"Type": "application/mp3"
}
推荐答案
如果列表中有两个或多个Metadata
项,并且将它们序列化为平面数组中的键-值对对象,如您在您的描述中所述问题,那么如果以后需要反序列化JSON,将很难分辨出哪些键值对属于在一起.最好改用像这样的二维数组结构:
If you have two or more Metadata
items in the list, and you serialized them to key-value pair objects in a flat array like you described in your question, then it would be difficult to tell which key-value pairs belong together if you need to deserialize the JSON later. It would be better to use a two-dimensional array structure like this instead:
{
"Metadata": [
[
{
"Key": "FirstName",
"Value": "ABC"
},
{
"Key": "LastName",
"Value": "XYZ"
}
],
[
{
"Key": "FirstName",
"Value": "DEF"
},
{
"Key": "LastName",
"Value": "MNL"
}
]
],
"Length": 25,
"Type": "application/mp3"
}
这是将执行此操作的转换器:
Here is a converter which will do that:
class ObjectToKvpArrayConverter<T> : JsonConverter where T : class
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JArray array = new JArray(
JObject.FromObject(value)
.Properties()
.Select(jp =>
new JObject(
new JProperty("Key", jp.Name),
new JProperty("Value", jp.Value)
)
)
);
array.WriteTo(writer);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject obj = new JObject(
JArray.Load(reader)
.Children<JObject>()
.Select(jo => new JProperty((string)jo["Key"], jo["Value"]))
);
T result = Activator.CreateInstance<T>();
serializer.Populate(obj.CreateReader(), result);
return result;
}
}
您可以像这样使用转换器:
You can use the converter like this:
var json = JsonConvert.SerializeObject(data, Formatting.Indented, new ObjectToKvpArrayConverter<Metadata>());
这是一个往返演示: https://dotnetfiddle.net/wx2e9d
这篇关于实施JsonConverter后,嵌套节点的格式不正确的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!