【问题标题】:C#: How to parse JSON enumsC#:如何解析 JSON 枚举
【发布时间】:2020-08-13 21:57:01
【问题描述】:

我有一个 JSON 字符串,我想根据 type 将其转换为多种可能的类型之一:

{
"type": "x",
"val": {...}
}

type 是一个枚举。 val 应该只匹配与该特定枚举键关联的类型。所有可能的有效val 都将具有相应的 C# 类。

是否可以一次性完成(而不是先只解析type,然后使用switch 尝试将val 解析为正确的类型)。

如何在无需手写代码来映射有效类型/值组合的情况下递归地完成此操作?例如。如果这个“JSON 枚举”模式是嵌套的,那么 val 对象包含另一个 type

谢谢

【问题讨论】:

  • 请展示至少两种类型的示例以及如何手动解析它们。更有趣的问题是您将如何处理反序列化的值,因为 C# 在编译时无法确定 val 的类型,因此您必须“点入”它,例如 deserialized.val.SomePropOfEnum1.SomePropOfEnum2
  • @CodeCaster 我应该有代表所有可能的val 的类。这不是动态 JSON,我知道它的结构。我问是否有可能不必手动将type 映射到正确的val - 可以用属性或泛型来完成吗?
  • 你有类,但没有属性也没有类型。鉴于您展示的 JSON,您将如何反序列化它?您不能使用泛型 (var o = JsonConvert.DeserializeObject<TypeWithVal<Foo>>),因为在阅读 JSON 之前,您不会知道自己需要 Foo。特别是如果它是递归的。所以你也许可以创建一个自定义转换器。但同样,请举例说明您将如何使用switch() 进行操作,并展示一些实际输入和用法。
  • 我没有例子,因为我目前正在研究最好的方法。我正在考虑使用var t = JsonConvert.DeserializeObject<KeyOnly>(jsonStr) 获取type,然后使用switch(t.type){"x": {parseVal(jsonStr)}}。我正在寻找类似 Rust 枚举的东西,它可以表达一个包含特定类型的枚举变体 doc.rust-lang.org/book/ch06-01-defining-an-enum.html

标签: c# json json.net


【解决方案1】:

你试过这个吗?我使用Newtonsoft.Json,它可以工作。我使用“X”和“Y”字符串并通过自定义转换器解析它们。

使用多态性来映射动态值。

查看示例代码:

public enum MyType
{
    X,
    Y
}

public class MyJsonWithType
{
    public MyType Type { get; set; }
    public IValueHeader Val { get; set; }
}

public interface IValueHeader
{

}

public class MyJsonDetail1 : IValueHeader
{
    public string SomeStr { get; set; }
}

public class MyJsonDetail2 : IValueHeader
{
    public int SomeInt { get; set; }
}

public class MyTypeConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(MyJsonWithType).GetTypeInfo().IsAssignableFrom(objectType.GetTypeInfo());
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.StartObject)
        {
            JObject item = JObject.Load(reader);
            if (item["Type"] != null)
            {
                var myType = item["Type"].ToObject<MyType>(serializer);
                if (myType == MyType.X)
                {
                    var xObject = item["Val"].ToObject<MyJsonDetail1>(serializer);
                    return new MyJsonWithType
                    {
                        Type = myType,
                        Val = xObject
                    };
                }
                else if (myType == MyType.Y)
                {
                    var yObject = item["Val"].ToObject<MyJsonDetail2>(serializer);
                    return new MyJsonWithType
                    {
                        Type = myType,
                        Val = yObject
                    };
                }
            }
        }

        return null;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

static void Main(string[] args)
{
    string newJson1 = "{\"Type\":\"X\",\"Val\":{\"SomeStr\":\"Test\"}}";
MyJsonWithType newTypeX = JsonConvert.DeserializeObject<MyJsonWithType>(newJson1, new MyTypeConverter());

    string newJson2 = "{\"Type\":\"Y\",\"Val\":{\"SomeInt\":5566}}";
MyJsonWithType newTypeY = JsonConvert.DeserializeObject<MyJsonWithType>(newJson2, new MyTypeConverter());


    DisplayMyTypeValue(newTypeX.Val);
    DisplayMyTypeValue(newTypeY.Val);
}

private static void DisplayMyTypeValue(IValueHeader val)
{
    if (val is MyJsonDetail1)
    {
        Console.WriteLine((val as MyJsonDetail1).SomeStr);
    }
    else if (val is MyJsonDetail2)
    {
        Console.WriteLine((val as MyJsonDetail2).SomeInt);
    }
}

【讨论】:

  • 我认为这不是正确的答案。因为Val 始终是MyJsonDetail,但问题是它的类型应该由 Type 值更改。
  • @donggas90 谢谢你的建议,我编辑了答案。
猜你喜欢
  • 2021-07-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-08-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多