Json.Net在使用我的JsonConverter之前自行转换了数据

3

在我的WPF代码中,我使用Newtonsoft.Json将json反序列化为我的模型。首先,我接收一个Json字符串('json'),然后将其解析为'message'。(我想反序列化的对象被包装在json字符串中的"data"字段中)。

Activity message = JObject.Parse(json)["data"].ToObject<Activity>();

我的Activity类使用了几个[JsonProperty]属性来生成其字段。其中一个是枚举类型,称为“ActivityType”。

[JsonProperty("type")]
[JsonConverter(typeof(ActivityTypeConverter))]
public ActivityType Type { get; set; }

public enum ActivityType {
    EmailOpen,
    LinkClick,
    Salesforce,
    Unsupported
};

public class ActivityTypeConverter : JsonConverter {

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var rawString = existingValue.ToString().ToLower();
        if (rawString.Contains("click"))
            return ActivityType.LinkClick;
        else if (rawString.Contains("salesforce"))
            return ActivityType.Salesforce;
        else if (rawString.Contains("email_open"))
            return ActivityType.EmailOpen;
        else
        {
            Console.WriteLine("unsupported " + rawString);
            return ActivityType.Unsupported;
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return !objectType.Equals(typeof(ActivityType));
    }

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

令人奇怪和沮丧的是,我知道具有 "type":"email_open" 的 json 对象被反序列化为 ActivityType.Unsupported,即使我的转换器应该将它们反序列化为 EmailOpen。
调试显示问题所在:json 字段 "type" 自动将 "email_open" 反序列化为 EmailOpen,并通过我的转换器发送。(因为 EmailOpen.ToString() 没有下划线,所以当我的条件检查下划线时会出错。) 那么我的问题是:为什么会进行转换而不使用我的转换器,我该如何停止它?我只想让它使用我的转换器。

1
那不应该是 objectType.Equals(typeof(ActivityType) (前面没有 !)吗?(或者直接写成 objectType == typeof(ActivityType) )? - dbc
@dbc 如果它已经是ActivityType,我不想进行转换。这是我试图阻止双重转换的尝试。(没有改变任何东西,因为我发现第一次转换甚至没有使用我的转换器) - thefistopher
作为自己编写转换器的替代方案,请参阅如何装饰JSON.NET StringEnumConverter - dbc
1个回答

2
我认为你的转换器已经被调用了,只是它没有起作用。问题在于,你没有从JsonReader reader读取新值,而是使用了existingValue的值。但是第二个值是正在反序列化的类中预先存在的属性值,而不是正在读取的值。
你需要按照Json.NET的StringEnumConverter的方式从reader中加载值。下面是一个版本,它可以通过子类化StringEnumConverter并将从文件中读取的值传递给基类来处理枚举的标准值:
public class ActivityTypeConverter : StringEnumConverter
{
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        bool isNullable = (Nullable.GetUnderlyingType(objectType) != null);
        Type type = (Nullable.GetUnderlyingType(objectType) ?? objectType);

        if (reader.TokenType == JsonToken.Null)
        {
            if (!isNullable)
                throw new JsonSerializationException();
            return null;
        }

        var token = JToken.Load(reader);
        if (token.Type == JTokenType.String)
        {
            var rawString = ((string)token).ToLower();
            if (rawString.Contains("click"))
                return ActivityType.LinkClick;
            else if (rawString.Contains("salesforce"))
                return ActivityType.Salesforce;
            else if (rawString.Contains("email_open"))
                return ActivityType.EmailOpen;
        }

        using (var subReader = token.CreateReader())
        {
            while (subReader.TokenType == JsonToken.None)
                subReader.Read();
            try
            {
                return base.ReadJson(subReader, objectType, existingValue, serializer); // Use base class to convert
            }
            catch (Exception ex)
            {
                return ActivityType.Unsupported;
            }
        }
    }

    public override bool CanConvert(Type objectType)
    {
        return objectType == typeof(ActivityType);
    }
}

哦,我明白了,你是对的,我把 existingValue 和预转换值搞混了。这可能是因为 EmailOpen 是枚举类型,而且它是非空的,所以它是第一个选项吧?不管怎样,现在一切都很好,谢谢! - thefistopher

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