【问题标题】:Cast to specific type depending on JSON attribute [duplicate]根据 JSON 属性转换为特定类型 [重复]
【发布时间】:2014-10-29 23:54:24
【问题描述】:

我有一个包含水果列表的字符串。我的 C# 程序不能直接使用 Fruit,它需要将它们转换为 Banana 或 Apple。 json 中的 constructor 属性中包含有关投射它们的信息。

注意:我使用的是 JSON.NET。

这就是我想要实现的目标:

    class Fruit{ public string constructor;}
    class Banana : Fruit { public int monkey;}
    class Apple : Fruit { public string shape;}

    string json = @"[
            {
                ""constructor"":""banana"",
                ""monkey"":1
            },
            {
                ""constructor"":""apple"",
                ""shape"":""round""
            }]";
        List<Fruit> fruitList = JsonConvert.DeserializeObject<List<Fruit>>(json);
        List<Banana> bananaList = new List<Banana>();
        List<Apple> appleList = new List<Apple>();
        foreach(var fruit in fruitList){
            if(fruit.constructor == "banana") bananaList.Add((Banana)fruit); //bug
            if(fruit.constructor == "apple") appleList.Add((Apple)fruit); //bug
        }

        //use bananaList and appleList for stuff

但是,我不能做演员表。有什么办法可以让bananaList 包含json 中所有构造函数属性设置为"banana" 且与appleList 相同的对象?


解决方案:

    public static string objToJson(object obj) {
        return JsonConvert.SerializeObject(obj, Formatting.Indented, new JsonSerializerSettings {
            TypeNameHandling = TypeNameHandling.Auto
        });
    }
    public static T jsonToObj<T>(string str) {
        return JsonConvert.DeserializeObject<T>(str, new JsonSerializerSettings {
            TypeNameHandling = TypeNameHandling.Auto
        });
    }

【问题讨论】:

  • 创建的对象是水果。类转换适用于 actual 对象,因此 Fruit 不能“更改”为 Banana/Apple - 只有在其他方式下才有效。话虽如此,很可能使用 CustomConverter 来处理这个问题(Json.NET 支持通过 $type 恢复类型),其中此类转换器会将每个对象适当地解析为 real Banana/Apple 实例。
  • 哇,看看这里:stackoverflow.com/questions/8030538/…(实际上是在搜索“自定义转换器”后的前几声中)
  • 讨厌你使用了我的答案但接受了不同的答案?

标签: c# json class json.net


【解决方案1】:

在 JSON.Net 中,您可以使用 $type 来指定要反序列化的类型:

string json = @"[
            {
                ""$type"":""Assembly.Namespace.banana"",
                ""monkey"":1
            },
            {
                ""$type"":""Assembly.Namespace.apple"",
                ""shape"":""round""
            }]";

【讨论】:

【解决方案2】:

您正在尝试进行多态反序列化,但您要求反序列化器反序列化为水果列表。您不能向上转换为 Apple/Banana,因为实例的类型为 Fruit。

如果您对 json 有控制权,则可以将包含类型信息的 json 保存在其中,反序列化器将自动创建正确的类型。

public class Fruit{  }
public class Banana : Fruit { public int monkey;}
public class Apple : Fruit { public string shape;}

class Program
{
    static void Main(string[] args)
    {
        var list = new List<Fruit>(new Fruit[] {new Banana(), new Apple()});

        var serializerSettings = new JsonSerializerSettings{ TypeNameHandling = TypeNameHandling.Auto };

        var json = JsonConvert.SerializeObject(list, serializerSettings);

        List<Fruit> fruitList = JsonConvert.DeserializeObject<List<Fruit>>(json, serializerSettings);
        List<Banana> bananaList = new List<Banana>();
        List<Apple> appleList = new List<Apple>();
        foreach (var fruit in fruitList)
        {
            if (fruit is Banana) bananaList.Add((Banana) fruit); 
            if (fruit is Apple) appleList.Add((Apple) fruit); 
        }
    }
}

你也可以使用一些 linq 比 foreach 更容易找到苹果/香蕉:

List<Banana> bananaList = fruitList.Where(f => f is Banana).Cast<Banana>().ToList();
List<Apple>  appleList  = fruitList.Where(f => f is Apple).Cast<Apple>().ToList();

或者您可以使用此处讨论的转换器Deserializing polymorphic json classes without type information using json.net

【讨论】:

  • “你可以保存带有类型信息的json”是什么意思?我到底在哪里使用我的 json 字符串?
  • @RainingChain:TypeNameHandling.Auto/Object 属性将在序列化期间写出 $type 信息,反序列化器可以使用这些信息将其反序列化为适当的类型。
  • 调试代码,查看JsonConvert.SerializeObject之后产生的json。你会看到
【解决方案3】:

JSON.NET 将只能解析它期望解析的内容。它无法假设它想要使用类的哪个多态子。

如果您可以控制源 JSON,则可以告诉序列化程序包含有关正在使用的类型的数据。

如果您不这样做,我相信您需要手动(或通过JTokens 或类似方法)阅读数据以找到constructor 属性,然后再告诉它解析正确的类型。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-11-25
    • 2021-07-21
    • 1970-01-01
    • 2020-01-06
    • 2016-06-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多