将JSON反序列化为多态对象的.NET集合,这些对象包含格式化为字符串的Point值

dm7nw8vv  于 2022-12-15  发布在  .NET
关注(0)|答案(1)|浏览(120)

我是JSON新手,希望将以下JSON转换为. NET中的对象列表。JSON在一个数组中包含两个不同的对象:

[  
   {  
      "type":"line",
      "a":"-1,5; 3,4",
      "b":"2,2; 5,7",
   },
   {  
      "type":"circle",
      "center":"0; 0",
      "radius":15.0,
   }
]

下面是相应的C#类:

public class Line : Shape
{
    public Point StartPoint { get; set; }
    public Point EndPoint { get; set; }
}

public class Circle : Shape
{
    public Point Center { get; set; }
    public double Radius { get; set; }
}

public abstract class Shape
{
    public string type { get; set; }
}

我不确定如何将JSON数据结构Map到LineCircle的C#类。我是否需要.NET中的其他类来桥接C#类和JSON数据结构?我也不确定在这种情况下如何处理坐标。是否可以仅使用.NET来完成此操作,或者我是否需要像JSON.NET这样的库?

**更新:**我想我知道如何反序列化多态对象了。我仍然有一个Map的问题。我如何告诉JSON.NET把一个字符串转换成一个System.Windows.Point和反过来?

vaj7vani

vaj7vani1#

剩下的问题是,您试图将包含一对数字的字符串反序列化为System.Windows.Point *,而这些数字在语言环境中使用逗号作为十进制分隔符 *。您需要为这种情况创建custom JsonConverter

public class PointConverter : JsonConverter
{
    readonly NumberFormatInfo numberFormatInfo = new NumberFormatInfo
    {
        NumberDecimalSeparator = ",",
        NumberGroupSeparator = ".",
    };

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Point) || objectType == typeof(Point?);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        switch (reader.MoveToContentAndAssert().TokenType)
        {
            case JsonToken.Null:
                return null;

            case JsonToken.String:
                {
                    var s = (string)reader.Value;

                    var values = s.Split(';');
                    if (values.Length != 2)
                        throw new JsonSerializationException(string.Format("Invalid Point format {0}", s));
                    try
                    {
                        return new Point(double.Parse(values[0], numberFormatInfo), double.Parse(values[1], numberFormatInfo));
                    }
                    catch (Exception ex)
                    {
                        throw new JsonSerializationException(string.Format("Invalid Point format {0}", s), ex);
                    }
                }

            default:
                throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
        }
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        writer.WriteValue(((Point)value).ToString(numberFormatInfo));
    }
}

public static partial class JsonExtensions
{
    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;
    }
}

然后反序列化如下:

var settings = new JsonSerializerSettings
{
    Converters = { new PointConverter() },
};
var root = JsonConvert.DeserializeObject<TRootObject>(jsonString, settings);

您需要使用JsonConverter,因为在Json.NET does support serializing objects as strings using their built-in TypeConverter中,用于将Point转换为字符串表示形式的PointConverter似乎存在错误。具体来说,此内置转换器在转换为字符串表示形式时依赖于区域性,但在转换回Point时不了解区域性。
这可以在PointConverter.ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)的参考源代码中看到:

public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
    if (value == null)
    {
        throw GetConvertFromException(value);
    }

    String source = value as string;

    if (source != null)
    {
        return Point.Parse(source);
    }

    return base.ConvertFrom(context, culture, value);
}

请注意,culture没有传递到Point.Parse()。相反,PointConverter.ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)在格式化时确实使用了传入的区域性。这种不一致性是一个bug。
为了确认这个错误,如果你尝试往返一个Point,比如说,在德国文化中,将会抛出一个异常。下面的代码将会抛出,演示了这个问题:

TypeDescriptor.GetConverter(typeof(Point)).ConvertFrom(null, CultureInfo.GetCultureInfo("de-DE"), TypeDescriptor.GetConverter(typeof(Point)).ConvertTo(null, CultureInfo.GetCultureInfo("de-DE"), new Point(-1.5, 3.4), typeof(string)))

相关问题