问题描述
在提到这个问题:
How我可以改变属性的名称时,序列化?
当然,伟大的,但我有鱼和熊掌兼得呢?
我正在寻找的是眼睛赏心悦目的方式以这样的方式该字符串可能包含一个属性有一个备用名称。
是这样的:
[BetterJsonProperty(属性名=foo_bar这样的名称)]
公共字符串FooBar的{搞定;组; }
两个
{
FooBar的:烨
}
和
{
foo_bar这样的名称:uhuh
}
将如预期反序列化。
由于没有属性的解决方案将工作或类似的类属性:
[AllowCStylePropertyNameAlternatives]
做到这一点的方法之一是创建一个自定义的<一个href=\"http://james.newtonking.com/json/help/?topic=html/T_Newtonsoft_Json_JsonConverter.htm\"><$c$c>JsonConverter$c$c>.这个想法是让转换器列举的对象,我们感兴趣的是JSON属性名,剥去名非字母数字字符,然后尝试匹配它们与通过反射的实际对象的属性。下面是它可能看起来在code:
公共类LaxPropertyNameMatchingConverter:JsonConverter
{
公众覆盖布尔CanConvert(类型的objectType)
{
返回objectType.IsClass;
} 公众覆盖布尔CanWrite
{
获得{返回false; }
} 公众覆盖对象ReadJson(JsonReader读者,类型的objectType,对象existingValue,JsonSerializer串行)
{
对象实例= objectType.GetConstructor(Type.EmptyTypes).Invoke(NULL);
的PropertyInfo [] =道具objectType.GetProperties(); JObject祚= JObject.Load(读卡器);
的foreach(在jo.Properties JProperty JP())
{
字符串名称= Regex.Replace(jp.Name,[^ A-ZA-Z0-9] +,); 的PropertyInfo道具= props.FirstOrDefault(PI =&GT;
pi.CanWrite&功放;&安培; string.Equals(pi.Name,姓名,StringComparison.OrdinalIgnoreCase)); 如果(丙!= NULL)
prop.SetValue(例如,jp.Value.ToObject(prop.PropertyType,序列化));
} 返回实例;
} 公共覆盖无效WriteJson(JsonWriter作家,对象的值,JsonSerializer串行)
{
抛出新NotImplementedException();
}
}
要与特定的类中使用自定义转换器,你可以装点这样的 [JsonConverter]
属性,该属性类:
[JsonConverter(typeof运算(LaxPropertyNameMatchingConverter))]
公共类MyClass的
{
公共字符串myProperty的{搞定;组; }
公共字符串MyOtherProperty {搞定;组; }
}
下面是在行动转换器的一个简单的演示:
类节目
{
静态无效的主要(字串[] args)
{
JSON字符串= @
[
{
我的财产:富,
我的-其他属性:酒吧,
},
{
(myProperty的):巴兹,
myOtherProperty:QUUX
},
{
myProperty的:嘶,
MY_OTHER_PROPERTY:砰
}
]; 清单&LT; MyClass的&GT;列表= JsonConvert.DeserializeObject&LT;名单,LT; MyClass的&GT;&GT;(JSON); 的foreach(MyClass的MC列表中)
{
Console.WriteLine(mc.MyProperty);
Console.WriteLine(mc.MyOtherProperty);
}
}
}
输出:
富
酒吧
巴兹
QUUX
嘶
砰
虽然这个解决方案应该在大多数情况下做的工作,还有一个更简单的解决方案的如果你确定用C直接的改变Json.Net源$ C $的想法。事实证明,你可以通过添加code刚一行到 Newtonsoft.Json.Serialization.JsonPropertyCollection
类完成同样的事情。在这个类中,有一个名为方法 GetClosestMatchProperty()
看起来像这样:
公共JsonProperty GetClosestMatchProperty(字符串propertyName的)
{
JsonProperty财产=的getProperty(propertyName的,StringComparison.Ordinal);
如果(属性== NULL)
属性=的getProperty(propertyName的,StringComparison.OrdinalIgnoreCase); 返回财产;
}
目前,其中该方法是由解串器称为来看, JsonPropertyCollection
包含类的所有属性被反序列化,而 propertyName的
参数包含所匹配的JSON属性名的名称。正如你所看到的,该方法首先尝试一个确切的名称匹配,那么它会尝试不区分大小写的匹配。因此,我们已经拥有了JSON和类属性名之间正在做一个多到一的映射。
如果您修改此法剥离所有非字母数字字符从属性名称匹配它之前,那么你就可以得到你想要的行为,而不需要特殊的转换器或属性。下面是修改code:
公共JsonProperty GetClosestMatchProperty(字符串propertyName的)
{
参数propertyName = Regex.Replace(propertyName的,[^ A-ZA-Z0-9] +,);
JsonProperty财产=的getProperty(propertyName的,StringComparison.Ordinal);
如果(属性== NULL)
属性=的getProperty(propertyName的,StringComparison.OrdinalIgnoreCase); 返回财产;
}
当然,修改源$ C $ C有它的问题为好,但我想这是值得一提。
In reference to this question:
How can I change property names when serializing?
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]
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:
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(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:
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.
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.
这篇关于反序列化替代属性名称的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!