使用Json.NET进行自定义反序列化

26

我有一个班级

public class Order
{
   public int Id { get; set; }
   public string ShippingMethod { get; set; }
}

我希望你能将下面的JSON数据反序列化为上述类/对象

string json = @"{
  'Id': 1,
  'ShippingMethod': {
     'Code': 'external_DHLExpressWorldwide',
     'Description': 'DHL ILS Express Worldwide'
  }
}";

我的想法是JSON中的ShippingMethod是一个对象,但我只想获取JSON中的ShippingMethod.Code,并将其作为Order类的string在反序列化期间传递给ShippingMethod

如何使用Json.NET实现这个目标?

我相信我可以使用CustomJsonConverter来实现它。但是我感到困惑。文档中的示例仅涉及WriteJson而不涉及ReadJson


所以 CodeDescriptionShippingMethod 中?它们如何序列化? - Tinwor
你只需要在同一级别上拥有"Id"和"Code"吗? - CodeNotFound
是的,那就是我的问题,也是我想要实现的目标。 - Habibillah
ShippingMethod 是 JSON 中的对象,也是 C# 类中的字符串。您可以如何映射它?您需要按照以下方式更改代码。 - Aruna
@Aruna 请看我映射的方式 https://dev59.com/fVkS5IYBdhLWcg3wDyn5#40439958 - Habibillah
3个回答

49

我刚刚使用我在问题中提到的JsonConverter解决了我的问题。以下是我的完整代码:

public class Order
{
    public int Id { get; set; }

    [JsonConverter(typeof(ShippingMethodConverter))]
    public string ShippingMethod { get; set; }
}

public class ShippingMethodConverter : JsonConverter
{

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

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return string.Empty;
        } 
        else if (reader.TokenType == JsonToken.String)
        {
            return serializer.Deserialize(reader, objectType);
        }
        else
        {
            JObject obj = JObject.Load(reader);
            if (obj["Code"] != null) 
                return obj["Code"].ToString();
            else 
                return serializer.Deserialize(reader, objectType);
        }
    }

    public override bool CanWrite
    {
        get { return false; }
    }

    public override bool CanConvert(Type objectType)
    {
        return false;
    }
}

17
 dynamic o = JsonConvert.DeserializeObject(json);
 var order = new Order
 {
     Id = o.Id,
     ShippingMethod = o.ShippingMethod.Code
 };

将对象反序列化为动态对象,然后通过访问动态对象的属性来填充Order对象。


1
+1 这是最好的清洁方式,没有属性污染和新类创建 :) - CodeNotFound
谢谢您的回答,但如果还有许多其他属性,它就不太适用了。 - Habibillah
你没有提到还有更多的属性需要管理。为了解决这个问题,你可以添加一个扩展方法来进行一次映射,然后你可以在任何地方使用它。但是你的解决方案也可以,只是对于两个属性来说代码行数太多了(在我看来)。 - Tinwor
是的,看起来有很多代码,但在实际应用中,它将使您的模型更加清晰。 - Habibillah

14

你可以使用 JsonPropertyJsonIgnore 属性来指导反序列化过程...这样你的模型就可以是:

public class Order
{
    public int Id { get; set; }

    [JsonIgnore]
    public string ShippingMethod
    {
        get
        {
            return (string)TempShippingMethod?["Code"];
        }
    }

    [JsonProperty("ShippingMethod")]
    private JObject TempShippingMethod { set; get; }
}

var res = JsonConvert.DeserializeObject<Order>(json);

是的,那只是一个小小的hack。我不喜欢这种方式,但我会点赞 :) 作为对你回答的尊重。 - Habibillah
@Habibillah,我也不喜欢你的回答,对于一个简单的事情来说太繁琐了。但是出于对你意见的尊重,我发布了我的答案的简化版本。 - L.B
没问题:)。这个问题看起来很简单,但在我的实际工作中,有很多属性和糟糕的JSON模式,它真的很值得。例如,保存订单时,您必须传递字符串,但是获取订单详细信息时,您得到了一个对象。还有许多其他情况。 - Habibillah
5
我不同意这是一个黑客技巧。JsonIgnore 就是为这种目的而设计的 —— 这意味着你不必重新编写序列化方法。我们可能缺少一些上下文信息,以了解为什么 OP 认为这不适合他的情况。 - Dave Hillier
当我使用[JsonProperty("...")]时,我会收到编译器错误CS0616:'JsonProperty'不是属性类。我需要改用[JsonPropertyName("...")]。我正在使用.Net 7.0。 - Tim
当将此对象反序列化时,请注意原始属性将会显示出来。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接