Json.NET不同的JSON结构的基础上,枚举值 [英] Json.NET different json structure, based on enum value
问题描述
我需要我的类转换成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:
-
您可以手动填写了超过
类型的所有属性名称和其他值
和TypeAdditionalData
然后写出来的自定义的输入
属性最后一次。例如:
You could manually write all property names and values other than
Type
andTypeAdditionalData
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屋!