Newtonsoft.Json忽略TypeConverters和/或JsonConverters,无论如何引用它们 [英] Newtonsoft.Json Ignores TypeConverters and/or JsonConverters, regardless of how it they're referenced

查看:89
本文介绍了Newtonsoft.Json忽略TypeConverters和/或JsonConverters,无论如何引用它们的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个枚举,例如:

[JsonConverter(typeof(MyJSONConverter))]
public enum MyEnum
{
    a,
    b,
    c
}

我在其上放了JsonConverter属性.

我已经定义了转换器类:

I have defined the converter class:

public class MyJSONConverter : JsonConverter<MyEnum>
{
    public MyJSONConverter()
    {
    }

    public override void WriteJson(JsonWriter writer, MyEnum value, JsonSerializer serializer)
    {
        serializer.Serialize(writer, value);
    }

    public override MyEnum ReadJson(JsonReader reader, Type objectType, MyEnum existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        switch (reader.Value)
        {
            case "a":
                return MyEnum.a;
            case "b":
                return MyEnum.b;
            case "c":
                return MyEnum.c;
        }

        return serializer.Deserialize<MyEnum>(reader);
    }

    public override bool CanRead
    {
        get
        {
            return true;
        }
    }

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

断点指示正在实例化该类.但是它从不调用任何成员方法或属性.

Breakpoints dictate the class is being instantiated. But it never calls any of the member methods or properties.

据我所知,仅此而已.它不起作用.

To my knowledge, that's all it should take to work. It does not work.

我尝试了TypeConverter和其他继承JsonConverter的类,包括一个没有泛型参数的类和StringEnumConverter.它们都不起作用.

I have tried TypeConverter and other classes that inherit JsonConverter, including the one without a generic argument, and the StringEnumConverter. None of them work.

虽然在我阅读的任何示例中似乎都没有必要,但我尝试将其作为转换器添加到我的设置中:

Although seemingly not necessary in any examples I have read, I have tried adding it as a converter to my settings:

        JsonSerializerSettings settings = new JsonSerializerSettings();
        settings.TypeNameHandling = TypeNameHandling.Auto;
        settings.ObjectCreationHandling = ObjectCreationHandling.Replace;
        settings.ContractResolver = new CShouldSerializeContractResolver();
        settings.Converters = new JsonConverter[] { new MyJSONConverter() };

        JsonSerializer = JsonSerializer.Create(settings);

如果我手动将其添加到我的转换器中,则在实例化JsonSerializer本身以及尝试反序列化时,转换器类将被实例化一次.似乎永远不会使用所创建的任何实例.

If I manually add it to my converters, the converter class gets instantiated once, upon instantiating the JsonSerializer itself, and upon attempting to deserialize. It seems to never use any instances that are created.

该错误明确指出尝试使用TypeConverter,但它们均不起作用.

The error clearly states to attempt to use a TypeConverter, but none of them work.

Could not convert string 'blahblah' to dictionary key type 'MyEnum'. 
Create a TypeConverter to convert from the string to the key type object. Path 
'Array[0].Keys.blahblah', line 1, position 1388.

在此处完成小提琴,但发生例外: https://dotnetfiddle.net/itCbER

Complete fiddle here, with exception occurring: https://dotnetfiddle.net/itCbER

为什么Newtonsoft不使用任何已记录的转换方法?

Why doesn't Newtonsoft use any of the documented methods of converting?

推荐答案

我在JsonConverter中使用了TypeSafeEnum来控制如何在我的代码和外部供应商API之间转换值.

I've used a TypeSafeEnum with JsonConverter to control how values are transposed between my code and an external vendor API.

首先,我们设置抽象的TypeSafeEnumBase类,该类将由多个对象使用.

First, we setup our abstract TypeSafeEnumBase class that will be used by multiple objects.

// define a base class for our type safe enum
public abstract class TypeSafeEnumBase
{
    // the types for Name and Value could be different if needed...
    protected readonly string Name;  // would be string value on typical enum
    protected readonly int Value; // would be int value on typical enum

    protected TypeSafeEnumBase(int value, string name)
    {
        this.Name = name;
        this.Value = value;
    }

    public override string ToString()
    {
        return Name;
    }
}

接下来,我们实现将从基本枚举继承的TypeSafeEnum.在本示例中,我实现了两个,您的示例中的MyEnum的类和MemberLanguage的一个类都从TypeSafeEnumBase继承.没有实际使用的enum,例如public enum MyEnum {}.我添加了MemberLanguage来帮助显示下一步中使用的TypeSafeEnumConversion类的好处.

Next, we implement our TypeSafeEnums that will inherit from the base enum. I've implemented two in this example, A class for MyEnum from your example and MemberLanguage, both inherit from the TypeSafeEnumBase. There is no actual enum being used, e.g. public enum MyEnum {}. I've added MemberLanguage to help show the benefit of the TypeSafeEnumConversion class that's used in the next step.

// define TypeSafeEnumBase that's a sealed class and inherits from the TypeSafeEnumBase
public sealed class MyEnum : TypeSafeEnumBase
{        
    // define 'enums'
    public static readonly MyEnum a = new MyEnum(1, "a");
    public static readonly MyEnum b = new MyEnum(2, "b");
    public static readonly MyEnum c = new MyEnum(3, "c");
    // add additional 'enums'...

    private MyEnum(int value, string name) :
        base(value, name)
    {
    }

    public static IEnumerable<MyEnum> MyEnums()
    {
        // this method only needed if you have to iterate over enums
        // and will need to be maintained as new MyEnums are added
        yield return a;
        yield return b;
        yield return c;
    }

    public static bool TryParse(int value, out MyEnum language)
    {
        return TryParse(value.ToString(), out language);
    }

    public static bool TryParse(string value, out MyEnum language)
    {
        try
        {
            language = Parse(value);
            return true;
        }
        catch
        {
            language = null;
            return false;
        }
    }

    public static MyEnum Parse(int value)
    {
        return Parse(value.ToString());
    }

    public static MyEnum Parse(string value)
    {
        switch (value)
        {
            case nameof(a):
            case "1":
                return MyEnum.a;
            case nameof(b):
            case "2":
                return MyEnum.b;
            case nameof(c):
            case "3":
                return MyEnum.c;
            default:
                return null;
                // throw new ArgumentOutOfRangeException(nameof(value), $"Unable to parse for value, '{value}'. Not found.");
        }
    }
}

public sealed class MemberLanguage : TypeSafeEnumBase
{
    // define 'enums'
    public static readonly MemberLanguage English = new MemberLanguage(1, "English");
    public static readonly MemberLanguage Spanish = new MemberLanguage(2, "Spanish");
    // add additional language types...
    // public static readonly MemberLanguage Farsi = new MemberLanguage(3, "Farsi");
    // etc...

    private MemberLanguage(int value, string name) :
        base(value, name)
    {
    }

    public static bool TryParse(int value, out MemberLanguage language)
    {
        return TryParse(value.ToString(), out language);
    }

    public static bool TryParse(string value, out MemberLanguage language)
    {
        try
        {
            language = Parse(value);
            return true;
        }
        catch
        {
            language = null;
            return false;
        }
    }

    public static MemberLanguage Parse(int value)
    {
        return Parse(value.ToString());
    }

    public static MemberLanguage Parse(string value)
    {
        switch (value)
        {
            case nameof(English):
            case "ENG":
            case "1":
                return MemberLanguage.English;
            case nameof(Spanish):
            case "SPN":
            case "2":
                return MemberLanguage.Spanish;
            default:
                return null;
                // throw new ArgumentOutOfRangeException(nameof(value), $"Unable to parse for value, '{value}'. Not found.");
        }
    }
}

接下来,我们为TypeSafeEnum创建一个转换帮助器. 通过创建此帮助程序,我们可以确保如果添加其他类型安全枚举,则无需更改JsonConverter.如果我们要添加更多内容,该类将得到更新,但JsonConverter不需要更新.

Next, we create a conversion helper for our TypeSafeEnums. By creating this helper we can ensure our JsonConverter will not need to change should we add additional type safe enums. Should we add more, this class will get updated but the JsonConverter will not need to be updated.

public class TypeSafeEnumConversion
{
    public static object ConvertToTypeSafeEnum(string name, string value)
    {
        object returnValue = null;
        switch (name)
        {
            case nameof(MemberLanguage):
            returnValue = MemberLanguage.Parse(value);
            break;
            case nameof(MyEnum):
            returnValue = MyEnum.Parse(value);
            break;
            //case nameof(SomeOtherEnum):
            // returnValue = SomeOtherEnum.Parse(value);
            // break;
        }
        return returnValue;
    }
}

现在,我们创建实现JsonConverter对象的自定义TypeSafeEnumJsonConverter类,并将调用我们的转换帮助器对象.再次,这使我们能够使此转换器对象保持隔离,并避免了在我们添加其他类型安全的枚举对象的情况下对其进行更改的必要性

Now we create our custom TypeSafeEnumJsonConverter class that implements the JsonConverter object, and will call our conversion helper object. Again, this allows us to keep this converter object isolated, and prevents the need for changes to it should we add additional type safe enum objects

public class TypeSafeEnumJsonConverter : JsonConverter
{        
    public override bool CanConvert(Type objectType)
    {
        var types = new[] { typeof(TypeSafeEnumBase) };
        return types.Any(t => t == objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        string name = objectType.Name;
        string value = serializer.Deserialize(reader).ToString();
        // call our custom conversion object
        return TypeSafeEnumConversion.ConvertToTypeSafeEnum(name, value);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        if (value == null
            && serializer.NullValueHandling == NullValueHandling.Ignore)
        {
            return;
        }
        writer.WriteValue(value.ToString()); 
    }
}

如何使用...

创建一个使用我们的TypeSafeEnum的对象.

Create an object that uses our TypeSafeEnums.

public class SomeThing
{
    public string Name { get; set; }

    [JsonProperty("my_enum")]
    [JsonConverter(typeof(TypeSafeEnumJsonConverter))]
    public MyEnum MyEnum { get; set; }

    [JsonProperty("language_preference")]
    [JsonConverter(typeof(TypeSafeEnumJsonConverter))]
    public MemberLanguage LanguagePreference { get; set; }  

    public override string ToString()
    {
        return $"{nameof(SomeThing)} : {MyEnum} : {LanguagePreference}";
    }    
}

用法...

internal class Program
{
    static void Main(string[] args)
    {
        IList<string> jsonDatas = new List<string>();
        // Serialize
        foreach (MyEnum e in MyEnum.MyEnums())
        {
            var thing = new SomeThing();
            thing.Name = e.ToString().ToUpper();
            thing.LanguagePreference = MemberLanguage.English;
            thing.MyEnum = e;
            string o = JsonConvert.SerializeObject(thing);
            jsonDatas.Add(o);
            Console.WriteLine(o);
        }
        // Deserialize
        foreach (string json in jsonDatas)
        {
            var thing = JsonConvert.DeserializeObject<SomeThing>(json);
            Console.WriteLine(thing.ToString());
        }
    }
}

// Serialized OUTPUT:
// {"Name":"A","my_enum":"a","language_preference":"English"}
// {"Name":"B","my_enum":"b","language_preference":"English"}
// {"Name":"C","my_enum":"c","language_preference":"English"}

// Deserialize OUTPUT:
// SomeThing : a : English
// SomeThing : b : English
// SomeThing : c : English

这篇关于Newtonsoft.Json忽略TypeConverters和/或JsonConverters,无论如何引用它们的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆