【问题标题】:Deserializing a nested object in JSON to a specific type将 JSON 中的嵌套对象反序列化为特定类型
【发布时间】:2014-07-29 17:47:22
【问题描述】:

我在 C# 端有一个类,看起来像这样:

[DataContract]
public class MyObject
{
    [DataMember]
    public SomeEnum FooType { get; set; }

    [DataMember]
    public FooBase MyFoo { get; set; }
}

基本上,属性FooType 中的值应该告诉您属性MyFoo 中存在从FooBase 派生的特定类型。

现在,如果我只想反序列化从 FooBase 派生的对象,我可以这样做:

var myFoo = JsonConvert.DeserializeObject(myJsonString, typeof(FooDerived)) as FooDerived;

但是我如何反序列化 MyObject 其中嵌套了 FooBase 对象,并且关于它是什么类型的信息只能通过首先部分反序列化对象来确定?

我认为这将需要一个从 JsonConverter 派生的自定义转换器,但我不完全确定如何使 ReadJson 在这里工作。

这样的?

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var result = new MyObject();
    while(reader.Read())
    {
        if(reader.TokenType == JsonToken.PropertyName)
        {
           var prop = reader.Value as string;
           if (prop == "FooType")
           {
               reader.Read();
               result.FooType = (SomeEnum)reader.ReadAsInt32();   // or something like that
           }
           if (prop == "MyFoo")
           {
               reader.Read();
               // now the reader.TokenType should be StartObject, but I can't
               // deserialize the object because I don't know what type it is
               // I might not have read "FooType" yet 
               // So I really need to pull this whole sub object out as a string
               // and deserialize it later???
           }
        }
    }
    return result;
}

【问题讨论】:

  • @BrianRogers:我不认为它是完全重复的(这里的多态部分是嵌套的,这使得这有点棘手),但我认为那里的答案确实给了我很好的解决方法问题。如果我在搜索时首先发现了这个问题,我可能就不需要问了。
  • 是的,我也是这么想的——它已经足够接近让您解决问题了。很高兴你成功了。

标签: c# json serialization json.net


【解决方案1】:

以此答案为灵感:https://stackoverflow.com/a/19308474/1250301

我想出了这样的东西:

public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
    var jObj = JObject.Load(reader);
    var foo = jObj["MyFoo"];
    var result = new MyObject(); 
    result.FooType = jObj["FooType"].ToObject<SomeEnum>();
    switch (result.FooType)
    {
        case SomeEnum.Value1:
            result.MyFoo = foo.ToObject<FooType1>();
            break;
        case SomeEnum.Value2:
            result.MyFoo = foo.ToObject<FooType2>();
            break;
        case SomeEnum.Value3:
            result.MyFoo = foo.ToObject<FooType3>();
            break;
        default:
            throw new Exception("Unknown FooType");
    }
    return result;
}

这里唯一的问题是,如果我向父对象添加新属性,我将需要手动映射它们。我想我也许可以这样做:

var parent = jObj.ToObject<MyObject>(); 

然后填写MyFoo 对象,但这只会再次调用ReadJson

【讨论】:

    【解决方案2】:

    我相信您可以使用Json.Linq 来完成此操作。我不是在电脑前测试这个,但我相信它是这样的:

    string fooTypeJson = JObject.Parse(myJsonString).SelectToken("FooType").ToString();
    FooType fooType = reader.DeserializeObject<FooType>(fooTypeJson);
    

    【讨论】:

    • 问题是,一旦你进入ReadJson,我就不太明白如何获取原始字符串。我可以尝试在JsonConverter 之外处理它,但这似乎是错误的处理方式。
    • 哦,您已经将它反序列化为它看起来像的父对象。那么为什么不直接从父对象调用它呢? FooType fooType = myFoo.FooType?
    猜你喜欢
    • 2016-02-17
    • 1970-01-01
    • 2016-12-12
    • 2017-12-28
    • 2019-12-08
    • 2017-12-28
    • 1970-01-01
    • 2020-09-29
    相关资源
    最近更新 更多