C# 反序列化时的备用属性名称
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/19792274/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Alternate property name while deserializing
提问by Martin
In reference to this question:
关于这个问题:
How can I change property names when serializing with Json.net?
Sure, great, but can I have the cake and eat it?
当然,太好了,但是我可以吃蛋糕吗?
What I'm looking for is an eye pleasing way have an alternate name for a property in such a way that the string may contain either.
我正在寻找的是一种令人赏心悦目的方式,它以字符串可能包含其中任何一个的方式为属性提供备用名称。
Something like:
就像是:
[BetterJsonProperty(PropertyName = "foo_bar")]
public string FooBar { get; set; }
Both
两个都
{
"FooBar": "yup"
}
and
和
{
"foo_bar":"uhuh"
}
would deserialize as expected.
会按预期反序列化。
As solution with no attribute would work or an attribute on the class like:
作为没有属性的解决方案或类上的属性,如:
[AllowCStylePropertyNameAlternatives]
采纳答案by Brian Rogers
One way to accomplish this is to create a custom JsonConverter
. The idea is to have the converter enumerate the JSON property names for objects we are interested in, strip the non-alphanumeric characters from the names and then try to match them up with the actual object properties via reflection. Here is how it might look in code:
实现此目的的一种方法是创建自定义JsonConverter
. 这个想法是让转换器枚举我们感兴趣的对象的 JSON 属性名称,从名称中去除非字母数字字符,然后尝试通过反射将它们与实际对象属性匹配。下面是它在代码中的样子:
public class LaxPropertyNameMatchingConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType.IsClass;
}
public override bool CanWrite
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object instance = objectType.GetConstructor(Type.EmptyTypes).Invoke(null);
PropertyInfo[] props = objectType.GetProperties();
JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties())
{
string name = Regex.Replace(jp.Name, "[^A-Za-z0-9]+", "");
PropertyInfo prop = props.FirstOrDefault(pi =>
pi.CanWrite && string.Equals(pi.Name, name, StringComparison.OrdinalIgnoreCase));
if (prop != null)
prop.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
}
return instance;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the custom converter with a particular class, you can decorate that class with a [JsonConverter]
attribute like this:
要将自定义转换器与特定类一起使用,您可以使用如下[JsonConverter]
属性装饰该类:
[JsonConverter(typeof(LaxPropertyNameMatchingConverter))]
public class MyClass
{
public string MyProperty { get; set; }
public string MyOtherProperty { get; set; }
}
Here is a simple demo of the converter in action:
这是转换器运行的简单演示:
class Program
{
static void Main(string[] args)
{
string json = @"
[
{
""my property"" : ""foo"",
""my-other-property"" : ""bar"",
},
{
""(myProperty)"" : ""baz"",
""myOtherProperty"" : ""quux""
},
{
""MyProperty"" : ""fizz"",
""MY_OTHER_PROPERTY"" : ""bang""
}
]";
List<MyClass> list = JsonConvert.DeserializeObject<List<MyClass>>(json);
foreach (MyClass mc in list)
{
Console.WriteLine(mc.MyProperty);
Console.WriteLine(mc.MyOtherProperty);
}
}
}
Output:
输出:
foo
bar
baz
quux
fizz
bang
While this solution should do the job in most cases, there is an even simpler solution if you are OK with the idea of changing the Json.Net source code directly. It turns out you can accomplish the same thing by adding just one line of code to the Newtonsoft.Json.Serialization.JsonPropertyCollection
class. In this class, there is a method called GetClosestMatchProperty()
which looks like this:
虽然这个解决方案在大多数情况下应该可以完成工作,但如果您同意直接更改 Json.Net 源代码的想法,还有一个更简单的解决方案。事实证明,您只需在Newtonsoft.Json.Serialization.JsonPropertyCollection
类中添加一行代码就可以完成同样的事情。在这个类中,有一个方法被调用GetClosestMatchProperty()
,它看起来像这样:
public JsonProperty GetClosestMatchProperty(string propertyName)
{
JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal);
if (property == null)
property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase);
return property;
}
At the point where this method is called by the deserializer, the JsonPropertyCollection
contains all the properties from the class being deserialized, and the propertyName
parameter contains the name of the JSON property name being matched. As you can see, the method first tries an exact name match, then it tries a case-insensitive match. So we already have a many-to-one mapping being done between the JSON and class property names.
在反序列化器调用此方法时,JsonPropertyCollection
包含来自正在反序列化的类的所有属性,并且propertyName
参数包含要匹配的 JSON 属性名称的名称。如您所见,该方法首先尝试精确名称匹配,然后尝试不区分大小写的匹配。所以我们已经在 JSON 和类属性名称之间完成了多对一的映射。
If you modify this method to strip out all non-alphanumeric characters from the property name prior to matching it, then you can get the behavior you desire, without any special converters or attributes needed. Here is the modified code:
如果您修改此方法以在匹配属性名称之前从属性名称中去除所有非字母数字字符,那么您可以获得所需的行为,而无需任何特殊转换器或属性。这是修改后的代码:
public JsonProperty GetClosestMatchProperty(string propertyName)
{
propertyName = Regex.Replace(propertyName, "[^A-Za-z0-9]+", "");
JsonProperty property = GetProperty(propertyName, StringComparison.Ordinal);
if (property == null)
property = GetProperty(propertyName, StringComparison.OrdinalIgnoreCase);
return property;
}
Of course, modifying the source code has its problems as well, but I figured it was worth a mention.
当然,修改源代码也有问题,但我认为值得一提。
回答by Adrian Petrescu
Another way of accomplishing this is intercepting the serialization/deserialization process early, by doing some overrides the JsonReader
and JsonWriter
实现此目的的另一种方法是通过执行一些覆盖JsonReader
和JsonWriter
public class CustomJsonWriter : JsonTextWriter
{
private readonly Dictionary<string, string> _backwardMappings;
public CustomJsonWriter(TextWriter writer, Dictionary<string, string> backwardMappings)
: base(writer)
{
_backwardMappings = backwardMappings;
}
public override void WritePropertyName(string name)
{
base.WritePropertyName(_backwardMappings[name]);
}
}
public class CustomJsonReader : JsonTextReader
{
private readonly Dictionary<string, string> _forwardMappings;
public CustomJsonReader(TextReader reader, Dictionary<string, string> forwardMappings )
: base(reader)
{
_forwardMappings = forwardMappings;
}
public override object Value
{
get
{
if (TokenType != JsonToken.PropertyName)
return base.Value;
return _forwardMappings[base.Value.ToString()];
}
}
}
After doing this, you can serialize by doing
执行此操作后,您可以通过执行序列化
var mappings = new Dictionary<string, string>
{
{"Property1", "Equivalent1"},
{"Property2", "Equivalent2"},
};
var builder = new StringBuilder();
JsonSerializer.Create().Serialize(new CustomJsonWriter(new StringWriter(builder), mappings), your_object);
and deserialize by doing
并通过做反序列化
var mappings = new Dictionary<string, string>
{
{"Equivalent1", "Property1"},
{"Equivalent2", "Property2"},
};
var txtReader = new CustomJsonReader(new StringReader(jsonString), mappings);
var your_object = JsonSerializer.Create().Deserialize<Your_Type>(txtReader);