Mar*_*eIV 7 c# json json.net system.text.json
我在获取新System.Text.Json的反序列化存储在只读属性上的集合时遇到了麻烦。
考虑这些类:
public class SomeItem {
public string Label { get; set; }
}
public class SomeObjectWithItems {
public string Label { get; set; }
// Note this property is read-only but the collection it points to is read/write
public ObservableCollection<SomeItem> Items { get; }
= new ObservableCollection<SomeItem>();
}
Run Code Online (Sandbox Code Playgroud)
这是JSON:
{
"Label": "First Set",
"Items": [
{
"Label": "Item 1"
},
{
"Label": "Item 2"
},
{
"Label": "Item 3"
},
{
"Label": "Item 4"
}
]
}
Run Code Online (Sandbox Code Playgroud)
这是我正在运行的代码...
var json = ...;
var obj = JsonSerializer.deserialize<SomeObjectWithItems>(json);
Debug.WriteLine($"Item Count for '{obj.label}': {obj.Items.Count}");
Run Code Online (Sandbox Code Playgroud)
以上输出如下:
Item Count for 'First Set': 0
Run Code Online (Sandbox Code Playgroud)
如果我更改Items为读/写,那么它可以工作,但是我们的许多模型都具有包含可变集合的只读属性,所以我想知道我们是否可以使用它。
注意:Json.NET 正确处理了这一点,在现有集合上内部调用了“Add”方法,而不是创建一个新集合,但我不知道除了为我们定义的所有类编写自定义转换器之外如何实现这一点。
这是为没有 setter 的集合设计的。为了避免添加到预先填充的集合(序列化器不会实例化)的问题,反序列化器使用“替换”语义,这要求集合具有设置器。
来源:https : //github.com/dotnet/corefx/issues/41433
目前有一个未解决的问题 Support adding to collections if no setter
https://github.com/dotnet/corefx/issues/39477
我的建议是Json.NET在这种情况下继续使用,除非您想编写自定义转换器。
来自 GitHub 的自定义转换器,我自己没有测试过:
class MagicConverter : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert) =>
!typeToConvert.IsAbstract &&
typeToConvert.GetConstructor(Type.EmptyTypes) != null &&
typeToConvert
.GetProperties()
.Where(x => !x.CanWrite)
.Where(x => x.PropertyType.IsGenericType)
.Select(x => new
{
Property = x,
CollectionInterface = x.PropertyType.GetGenericInterfaces(typeof(ICollection<>)).FirstOrDefault()
})
.Where(x => x.CollectionInterface != null)
.Any();
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options) => (JsonConverter)Activator.CreateInstance(typeof(SuperMagicConverter<>).MakeGenericType(typeToConvert))!;
class SuperMagicConverter<T> : JsonConverter<T> where T : new()
{
readonly Dictionary<string, (Type PropertyType, Action<T, object>? Setter, Action<T, object>? Adder)> PropertyHandlers;
public SuperMagicConverter()
{
PropertyHandlers = typeof(T)
.GetProperties()
.Select(x => new
{
Property = x,
CollectionInterface = !x.CanWrite && x.PropertyType.IsGenericType ? x.PropertyType.GetGenericInterfaces(typeof(ICollection<>)).FirstOrDefault() : null
})
.Select(x =>
{
var tParam = Expression.Parameter(typeof(T));
var objParam = Expression.Parameter(typeof(object));
Action<T, object>? setter = null;
Action<T, object>? adder = null;
Type? propertyType = null;
if (x.Property.CanWrite)
{
propertyType = x.Property.PropertyType;
setter = Expression.Lambda<Action<T, object>>(
Expression.Assign(
Expression.Property(tParam, x.Property),
Expression.Convert(objParam, propertyType)),
tParam,
objParam)
.Compile();
}
else
{
if (x.CollectionInterface != null)
{
propertyType = x.CollectionInterface.GetGenericArguments()[0];
adder = Expression.Lambda<Action<T, object>>(
Expression.Call(
Expression.Property(tParam, x.Property),
x.CollectionInterface.GetMethod("Add"),
Expression.Convert(objParam, propertyType)),
tParam,
objParam)
.Compile();
}
}
return new
{
x.Property.Name,
setter,
adder,
propertyType
};
})
.Where(x => x.propertyType != null)
.ToDictionary(x => x.Name, x => (x.propertyType!, x.setter, x.adder));
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => throw new NotImplementedException();
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var item = new T();
while (reader.Read())
{
if (reader.TokenType == JsonTokenType.EndObject)
{
break;
}
if (reader.TokenType == JsonTokenType.PropertyName)
{
if (PropertyHandlers.TryGetValue(reader.GetString(), out var handler))
{
if (!reader.Read())
{
throw new JsonException($"Bad JSON");
}
if (handler.Setter != null)
{
handler.Setter(item, JsonSerializer.Deserialize(ref reader, handler.PropertyType, options));
}
else
{
if (reader.TokenType == JsonTokenType.StartArray)
{
while (true)
{
if (!reader.Read())
{
throw new JsonException($"Bad JSON");
}
if (reader.TokenType == JsonTokenType.EndArray)
{
break;
}
handler.Adder!(item, JsonSerializer.Deserialize(ref reader, handler.PropertyType, options));
}
}
else
{
reader.Skip();
}
}
}
else
{
reader.Skip();
}
}
}
return item;
}
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
var options = new JsonSerializerOptions { Converters = { new MagicConverter() } };
var adsfsdf = System.Text.Json.JsonSerializer.Deserialize<Grrrr>("{\"Meow\":[1,2,3]}", options);
var adsfsdf2 = System.Text.Json.JsonSerializer.Deserialize<Grrrr>("{\"Meow\":null}", options);
var adsfsdf3 = System.Text.Json.JsonSerializer.Deserialize<Grrrr>("{\"Meow\":[1,2,3],\"Rawr\":\"asdf\"}", options);
var adsfsdf4 = System.Text.Json.JsonSerializer.Deserialize<Grrrr>("{\"Meow\":[1,2,3],\"Rawr\":null}", options);
var adsfsdf5 = System.Text.Json.JsonSerializer.Deserialize<Grrrr>("{\"Meow\":[1,2,3],\"Rawr\":\"asdf\",\"SubGrr\":{\"Meow\":[1,2,3],\"Rawr\":\"asdf\"}}", options);
Run Code Online (Sandbox Code Playgroud)
来源:
https://github.com/dotnet/runtime/issues/30258#issuecomment-564847072
| 归档时间: |
|
| 查看次数: |
1745 次 |
| 最近记录: |