我有正确使用我的“InternedString”转换器的现有属性:
[JsonProperty(ItemConverterType=typeof(InternedString))]
public string[] prizeTypes;
是否有一种简单的方法可以将其应用于 jagged 二维字符串数组,而无需编写自己的 string[][]
转换器?
[JsonProperty(ItemConverterType=typeof(InternedString))]
public string[][] prizeTypesByRoomType;
(它给了我一个“无法将数组转换为字符串”的异常,这是显而易见的,但我有点希望它可以递归地应用该类型)
答案 0 :(得分:2)
Json.NET 没有使用属性将自定义 JsonConverter
应用于交错数组(例如 string [][]
)的项目的功能。 ItemConverterType
仅适用于最外层数组的项。
您可以做的是应用 decorator pattern 来创建一个 custom JsonConverter
,该 How to apply a custom JsonConverter to the values inside a list inside a dictionary? 为任意深度的锯齿状数组的最内部项目包装了一些内部项目转换器。
首先定义如下转换器:
public class JaggedArrayItemConverterDecorator : JsonConverter
{
readonly JsonConverter itemConverter;
public JaggedArrayItemConverterDecorator(Type type) =>
itemConverter = (JsonConverter)Activator.CreateInstance(type ?? throw new ArgumentNullException());
public override bool CanConvert(Type objectType) => objectType.IsJaggedRankOneArray(out var itemType) && itemConverter.CanConvert(itemType);
public override bool CanRead => itemConverter.CanRead;
public override bool CanWrite => itemConverter.CanWrite;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
return null;
if (reader.TokenType != JsonToken.StartArray)
throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, JsonToken.StartArray));
var itemType = objectType.GetElementType();
IList list = (IList)serializer.ContractResolver.ResolveContract(typeof(List<>).MakeGenericType(itemType)).DefaultCreator();
while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray)
{
if (itemType.IsArray)
list.Add(ReadJson(reader, itemType, null, serializer));
else
list.Add(itemConverter.ReadJson(reader, itemType, null, serializer));
}
var array = Array.CreateInstance(itemType, list.Count);
list.CopyTo(array, 0);
return array;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var itemType = value.GetType().GetElementType();
writer.WriteStartArray();
foreach (var item in (IList)value)
{
if (item == null)
writer.WriteNull();
else if (itemType.IsArray)
WriteJson(writer, item, serializer);
else
itemConverter.WriteJson(writer, item, serializer);
}
writer.WriteEndArray();
}
}
public static partial class JsonExtensions
{
public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
reader.ReadAndAssert().MoveToContentAndAssert();
public static JsonReader MoveToContentAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (reader.TokenType == JsonToken.None) // Skip past beginning of stream.
reader.ReadAndAssert();
while (reader.TokenType == JsonToken.Comment) // Skip past comments.
reader.ReadAndAssert();
return reader;
}
public static JsonReader ReadAndAssert(this JsonReader reader)
{
if (reader == null)
throw new ArgumentNullException();
if (!reader.Read())
throw new JsonReaderException("Unexpected end of JSON stream.");
return reader;
}
}
public static class TypeExtensions
{
public static bool IsJaggedRankOneArray(this Type type, out Type innermostItemType)
{
innermostItemType = null;
var currentType = type ?? throw new ArgumentNullException(nameof(type));
while (currentType.IsArray)
{
if (currentType.GetArrayRank() != 1)
return false;
currentType = currentType.GetElementType();
}
if (currentType != type)
{
innermostItemType = currentType;
return true;
}
return false;
}
}
然后您可以将其应用于模型中任意深度的锯齿状数组,如下所示:
public class Model
{
[JsonProperty(ItemConverterType=typeof(InternedString))] // For 1d arrays you can use either ItemConverterType=typeof(InternedString) or JaggedArrayItemConverterDecorator
public string[] prizeTypes { get; set; }
[JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
public string[] prizeTypesByRoomType { get; set; }
[JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
public string[][] prizeTypesByRoomType2d { get; set; }
[JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
public string[][][] prizeTypesByRoomType3d { get; set; }
[JsonConverter(typeof(JaggedArrayItemConverterDecorator), typeof(InternedString))]
public string[][][][] prizeTypesByRoomType4d { get; set; }
[JsonProperty(ItemConverterType=typeof(InternedString))] // ItemConverterType works for multidimensional arrays
public string[,] prizeTypesMultidimensional { get; set; }
}
注意事项:
转换器是为数组而不是列表实现的。如果您需要支持锯齿状列表,请参阅here。
转换器未针对多维数组(例如 string [,]
)或包含多维数组的锯齿状数组实现。但是,ItemConverterType
实际上支持多维数组,因此除非您有多维数组的锯齿状数组,反之亦然,否则您应该全部设置。
演示小提琴{{3}}。