Json.NET不同的JSON结构的基础上,枚举值 [英] Json.NET different json structure, based on enum value

查看:305
本文介绍了Json.NET不同的JSON结构的基础上,枚举值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要我的类转换成JSON和我使用Json.NET。但是,我可以有不同的JSON的结构,如:

I need to convert my class to JSON and I use Json.NET. But I can have different JSON structures, like:

{
    name: "Name",
    type: "simple1",
    value: 100
};

{
    name: "Name",
    type: {
        optional1: {
            setting1: "s1",
            setting2: "s2",
            ///etc.
    },
    value: 100
};



我的C#代码为:

My C# code is:

public class Configuration
{
    [JsonProperty(PropertyName = "name")]
    public string Name{ get; set; }

    [JsonProperty(PropertyName = "type")]
    public MyEnumTypes Type { get; set; }

    public OptionalType TypeAdditionalData { get; set; }

    [JsonProperty(PropertyName = "value")]
    public int Value { get; set; }
    public bool ShouldSerializeType()
    {
        OptionalSettingsAttribute optionalSettingsAttr = this.Type.GetAttributeOfType<OptionalSettingsAttribute>();
        return optionalSettingsAttr == null;
    }

    public bool ShouldSerializeTypeAdditionalData()
    {
        OptionalSettingsAttribute optionalSettingsAttr = this.Type.GetAttributeOfType<OptionalSettingsAttribute>();
        return optionalSettingsAttr != null;
    }
}

public enum MyEnumTypes 
{
    [EnumMember(Value = "simple1")]
    Simple1,

    [EnumMember(Value = "simple2")]
    Simple2,

    [OptionalSettingsAttribute]
    [EnumMember(Value = "optional1")]
    Optional1,

    [EnumMember(Value = "optional2")]
    [OptionalSettingsAttribute]
    Optional2
}

我的想法是,当 Configuration.Type - 价值没有属性 OptionalSettingsAttribute - 连载为键入:simple1。否则 - 使用 Configuration.Type - 值类型的值键(类型:{optional1:{}} )和在 Configuration.TypeAdditionalData optional1 - 值(如2简单的JSON以上)

My idea was when Configuration.Type - value hasn't attribute OptionalSettingsAttribute - to serialize it as type: "simple1". Otherwise - to use Configuration.Type - value as type's value key (type: { optional1: {} }) and value in Configuration.TypeAdditionalData as optional1 - value (like 2 simple JSON above).

我试图创建一个自定义的转换器,如:

I tried to create a custom Converter, like:

public class ConfigurationCustomConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(Configuration).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        return serializer.Deserialize<Configuration>(reader);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        //my changes here

        serializer.Serialize(writer, value);
    }



但是,当我加入 [JsonConverter(typeof运算(ConfigurationCustomConverter ))] 属性配置类:

[JsonConverter(typeof(ConfigurationCustomConverter))]
public class Configuration

和名为 JsonConvert.SerializeObject(configurationObj); 我收到一个错误:

and called JsonConvert.SerializeObject(configurationObj); I received next error:

自参考环路检测

用型配置。路径'。

Self referencing loop detected with type 'Configuration'. Path ''.

你有什么想法如何更改我的代码序列化我班2个不同的JSON的结构?
注意:我不会使用相同的类反序列化的JSON

Do you have any ideas how to change my code to serialize my class to 2 different JSON structures? Note: I won't use the same class to deserialize the JSON.

感谢您

推荐答案

你所得到的的原因自参考环路检测例外是的 WriteJson 与转换器的方法自称递归。当您将转换器类型使用 [JsonConverter(typeof运算(ConfigurationCustomConverter))] WriteJson()方法将无条件更换 Json.NET的默认实现。因此,你内心的呼唤:

The reason you are getting the Self referencing loop detected exception is that the WriteJson method of your converter is calling itself recursively. When you apply a converter to a type using [JsonConverter(typeof(ConfigurationCustomConverter))], the WriteJson() method will unconditionally replace Json.NET's default implementation. Thus your inner call:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    //my changes here
    serializer.Serialize(writer, value);
}



会导致堆栈溢出。 Json.NET注意到了这个,而是抛出看到异常。有关详细信息,请参见 JSON.Net使用时抛出StackOverflowException [JsonConvert()] 。设置 ReferenceLoopHandling.Ignore 只是导致无限递归被跳过,留下你的对象是空的。

would cause a stack overflow. Json.NET notices this and instead throws the exception you see. For more details, see JSON.Net throws StackOverflowException when using [JsonConvert()]. Setting ReferenceLoopHandling.Ignore simply causes the infinite recursion to be skipped, leaving your object empty.

您有几个选项来解决这个问题:

You have a few options to solve this problem:


  1. 您可以手动填写了超过类型的所有属性名称和其他值 TypeAdditionalData 然后写出来的自定义的输入属性最后一次。例如:

  1. You could manually write all property names and values other than Type and TypeAdditionalData then write out the custom "type" property last. For instance:

[JsonConverter(typeof(ConfigurationConverter))]
public class Configuration
{
    [JsonProperty(PropertyName = "name")]
    public string Name { get; set; }

    public MyEnumTypes Type { get; set; }

    public OptionalType TypeAdditionalData { get; set; }

    [JsonProperty(PropertyName = "value")]
    public int Value { get; set; }
}

class ConfigurationConverter : JsonConverter
{
    const string typeName = "type";

    public override bool CanConvert(Type objectType)
    {
        return typeof(Configuration).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var config = (existingValue as Configuration ?? (Configuration)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator());

        // Populate the regular property values.
        var obj = JObject.Load(reader);
        var type = obj.RemoveProperty(typeName);
        using (var subReader = obj.CreateReader())
            serializer.Populate(subReader, config);

        // Populate Type and OptionalType
        if (type is JValue) // Primitive value
        {
            config.Type = type.ToObject<MyEnumTypes>(serializer);
        }
        else
        {
            var dictionary = type.ToObject<Dictionary<MyEnumTypes, OptionalType>>(serializer);
            if (dictionary.Count > 0)
            {
                config.Type = dictionary.Keys.First();
                config.TypeAdditionalData = dictionary.Values.First();
            }
        }

        return config;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var config = (Configuration)value;
        var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(config.GetType());
        writer.WriteStartObject();
        foreach (var property in contract.Properties
            .Where(p => p.Writable && (p.ShouldSerialize == null || p.ShouldSerialize(config)) && !p.Ignored))
        {
            if (property.UnderlyingName == "Type" || property.UnderlyingName == "TypeAdditionalData")
                continue;
            var propertyValue = property.ValueProvider.GetValue(config);
            if (propertyValue == null && serializer.NullValueHandling == NullValueHandling.Ignore)
                continue;
            writer.WritePropertyName(property.PropertyName);
            serializer.Serialize(writer, propertyValue);
        }
        writer.WritePropertyName(typeName);
        if (config.Type.GetCustomAttributeOfEnum<OptionalSettingsAttribute>() == null)
        {
            serializer.Serialize(writer, config.Type);
        }
        else
        {
            var dictionary = new Dictionary<MyEnumTypes, OptionalType>
            {
                { config.Type, config.TypeAdditionalData },
            };
            serializer.Serialize(writer, dictionary);
        }
        writer.WriteEndObject();
    }
}

public class OptionalType
{
    public string setting1 { get; set; }
}

public class OptionalSettingsAttribute : System.Attribute
{
    public OptionalSettingsAttribute()
    {
    }
}

[JsonConverter(typeof(StringEnumConverter))]
public enum MyEnumTypes
{
    [EnumMember(Value = "simple1")]
    Simple1,

    [EnumMember(Value = "simple2")]
    Simple2,

    [OptionalSettingsAttribute]
    [EnumMember(Value = "optional1")]
    Optional1,

    [EnumMember(Value = "optional2")]
    [OptionalSettingsAttribute]
    Optional2
}

public static class EnumExtensions
{
    public static TAttribute GetCustomAttributeOfEnum<TAttribute>(this Enum value)
        where TAttribute : System.Attribute
    {
        var type = value.GetType();
        var memInfo = type.GetMember(value.ToString());
        return memInfo[0].GetCustomAttribute<TAttribute>();
    }
}

public static class JsonExtensions
{
    public static JToken RemoveProperty(this JObject obj, string name)
    {
        if (obj == null)
            return null;
        var property = obj.Property(name);
        if (property == null)
            return null;
        var value = property.Value;
        property.Remove();
        property.Value = null;
        return value;
    }
}



通知我添加 [JsonConverter (typeof运算(StringEnumConverter))] 您枚举。这保证了类型总是作为字符串写入。

Notice I added [JsonConverter(typeof(StringEnumConverter))] to your enum. This ensures the type is always written as a string.

小提琴。

您可以通过在的> JSON.Net抛出stackOverflowException,生成一个默认的序列化,修改为必需的,它写出来的。

You could disable recursive calls to the converter via the technique shown in JSON.Net throws StackOverflowException when using [JsonConvert()], generate a default serialization, modify it as required, and write it out.

您可能避免使用转换器完全由标记键入 TypeAdditionalData [JsonIgnore] 和引入更多的私人财产序列化和反序列化输入

You could avoid the use of a converter entirely by marking Type and TypeAdditionalData as [JsonIgnore] and introducing an additional private property to serialize and deserialize "type":

public class Configuration
{
    [JsonProperty(PropertyName = "name")]
    public string Name { get; set; }

    [JsonIgnore]
    public MyEnumTypes Type { get; set; }

    [JsonIgnore]
    public OptionalType TypeAdditionalData { get; set; }

    [JsonProperty("type")]
    JToken SerializedType
    {
        get
        {
            if (Type.GetCustomAttributeOfEnum<OptionalSettingsAttribute>() == null)
            {
                return JToken.FromObject(Type);
            }
            else
            {
                var dictionary = new Dictionary<MyEnumTypes, OptionalType>
                {
                    { Type, TypeAdditionalData },
                };
                return JToken.FromObject(dictionary);
            }
        }
        set
        {
            if (value == null || value.Type == JTokenType.Null)
            {
                TypeAdditionalData = null;
                Type = default(MyEnumTypes);
            }
            else if (value is JValue)
            {
                Type = value.ToObject<MyEnumTypes>();
            }
            else
            {
                var dictionary = value.ToObject<Dictionary<MyEnumTypes, OptionalType>>();
                if (dictionary.Count > 0)
                {
                    Type = dictionary.Keys.First();
                    TypeAdditionalData = dictionary.Values.First();
                }
            }
        }
    }

    [JsonProperty(PropertyName = "value")]
    public int Value { get; set; }
}


这篇关于Json.NET不同的JSON结构的基础上,枚举值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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