NewtonSoft JsonConvert - 反序列化一个看起来像 { “@nil”: “true” } 的 JSON 对象

Rah*_*hul 1 json json.net

我正在尝试反序列化休息服务返回的以下 JSON:

[{
"Vehicle": {
  "Id": "1",
   "RenewalDate": {
    "@nil": "true"
  }     
}}]
Run Code Online (Sandbox Code Playgroud)

该服务似乎将 XML 转换为 JSON,因此 XML nil 作为 JSON 字符串的一部分包含在内。

请让我知道如何在 Newtonsoft 反序列化方法中处理这个问题?

如果续订日期包含在字符串中,反序列化工作正常。

dbc*_*dbc 5

如果您的问题没有最小、完整和可验证的示例,我将假设您正在尝试反序列化为如下所示的类列表:

public class Vehicle
{
    public string Id { get; set; }

    [XmlElement(IsNullable = true)]
    public DateTime? RenewalDate { get; set; }
}

public class RootObject
{
    public Vehicle Vehicle { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

并且,该属性的反序列化失败,"RenewalDate"因为不是null出现在 JSON 中的值,而是xsi:nil="true"存在包含翻译属性的对象。

解决此问题的一种方法是引入以下自定义JsonConverter

public class NullableStructConverter<T> : JsonConverter where T : struct
{
    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(Nullable<T>);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var underlyingType = Nullable.GetUnderlyingType(objectType);
        if (underlyingType == null)
            throw new InvalidOperationException(string.Format("Type {0} is not nullable", objectType));
        var token = JToken.Load(reader);
        if (token.Type == JTokenType.Null)
            return null;
        if (token.WasNilXmlElement())
            return null;
        return token.ToObject(underlyingType, serializer);
    }

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

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

public static partial class JTokenExtensions
{
    public static bool WasNilXmlElement(this JToken token)
    {
        if (token == null)
            return true;
        if (token.Type == JTokenType.Null)
            return true;
        var obj = token as JObject;
        if (obj != null)
        {
            // Check if all properties were translated from XML attributes
            // and one was translated from xsi:nil = true
            // There might be namespaces present as well, e.g.
            // "@xmlns:p3": "http://www.w3.org/2001/XMLSchema-instance"
            if (obj.Properties().All(p => p.Name.StartsWith("@"))
                && obj.Properties().Any(p => p.Name == "@nil" || p.Name.EndsWith(":nil") && p.Value.ToString() == "true"))
                return true;
        }
        return false;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后反序列化如下:

var settings = new JsonSerializerSettings 
{ 
    Converters = { new NullableStructConverter<DateTime>() } 
    // Whatever other settings you require.
};
var root = JsonConvert.DeserializeObject<RootObject[]>(json, settings);
Run Code Online (Sandbox Code Playgroud)

工作.Net 小提琴

另一种选择是将 JSON 加载到JToken层次结构中,将所有从nilXML 元素转换为nullJSON 值的JSON 对象替换为JSON 值,然后最终反序列化为您的模型。首先介绍WasNilXmlElement()一下第一个方案中使用的扩展方法:

public static partial class JTokenExtensions
{
    public static JToken ReplaceNilXmlElementObjectsWithNull(this JToken root)
    {
        var rootContainer = root as JContainer;
        if (rootContainer == null)
            return root;
        var list = rootContainer.DescendantsAndSelf()
            .OfType<JObject>()
            .Where(o => o.WasNilXmlElement())
            .ToList();
        foreach (var obj in list)
        {
            var replacement = JValue.CreateNull();
            if (obj.Parent != null)
                obj.Replace(replacement);
            if (root == obj)
                root = replacement;
        }
        return root;
    }
}
Run Code Online (Sandbox Code Playgroud)

并反序列化如下:

var settings = new JsonSerializerSettings
{
    // Whatever settings you require.
};
var root = JsonConvert.DeserializeObject<JToken>(json, settings)
    .ReplaceNilXmlElementObjectsWithNull()
    .ToObject<RootObject[]>(JsonSerializer.CreateDefault(settings));
Run Code Online (Sandbox Code Playgroud)

此解决方案避免了JsonConverter对每个可为空类型的需求。工作小提琴 #2