C# JavaScriptSerializer.Deserialize - 如何更改字段名称
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1100191/
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
JavaScriptSerializer.Deserialize - how to change field names
提问by Anthony
Summary: How do I map a field name in JSON data to a field name of a .Net object when using JavaScriptSerializer.Deserialize ?
摘要: 使用 JavaScriptSerializer.Deserialize 时,如何将 JSON 数据中的字段名称映射到 .Net 对象的字段名称?
Longer version: I have the following JSON data coming to me from a server API (Not coded in .Net)
更长的版本:我有以下来自服务器 API 的 JSON 数据(未在 .Net 中编码)
{"user_id":1234, "detail_level":"low"}
I have the following C# object for it:
我有以下 C# 对象:
[Serializable]
public class DataObject
{
[XmlElement("user_id")]
public int UserId { get; set; }
[XmlElement("detail_level")]
public DetailLevel DetailLevel { get; set; }
}
Where DetailLevel is an enum with "Low" as one of the values.
其中 DetailLevel 是一个枚举,其中“低”作为值之一。
This test fails:
此测试失败:
[TestMethod]
public void DataObjectSimpleParseTest()
{
JavaScriptSerializer serializer = new JavaScriptSerializer();
DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
Assert.IsNotNull(dataObject);
Assert.AreEqual(DetailLevel.Low, dataObject.DetailLevel);
Assert.AreEqual(1234, dataObject.UserId);
}
And the last two asserts fail, since there is no data in those fields. If I change the JSON data to
最后两个断言失败,因为这些字段中没有数据。如果我将 JSON 数据更改为
{"userid":1234, "detaillevel":"low"}
Then it passes. But I can't change the server's behaviour, and I want the client classes to have well-named properties in the C# idiom. I can't use LINQ to JSON since I want it to work outside of Silverlight. It looks like the XmlElement tags are having no effect. I don't know where I got the idea they were relevant at all, they probably aren't.
然后就过去了。但是我无法更改服务器的行为,并且我希望客户端类在 C# 习惯用法中具有命名良好的属性。我不能使用 LINQ to JSON,因为我希望它在 Silverlight 之外工作。看起来 XmlElement 标签没有效果。我不知道我从哪里得到的想法它们完全相关,它们可能不是。
How do you do field name mapping in JavaScriptSerializer? Can it be done at all?
你如何在 JavaScriptSerializer 中进行字段名称映射?完全可以做到吗?
回答by Anthony
I took another try at it, using the DataContractJsonSerializerclass. This solves it:
我再次尝试使用DataContractJsonSerializer类。这解决了它:
The code looks like this:
代码如下所示:
using System.Runtime.Serialization;
[DataContract]
public class DataObject
{
[DataMember(Name = "user_id")]
public int UserId { get; set; }
[DataMember(Name = "detail_level")]
public string DetailLevel { get; set; }
}
And the test is:
测试是:
using System.Runtime.Serialization.Json;
[TestMethod]
public void DataObjectSimpleParseTest()
{
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(DataObject));
MemoryStream ms = new MemoryStream(Encoding.Unicode.GetBytes(JsonData));
DataObject dataObject = serializer.ReadObject(ms) as DataObject;
Assert.IsNotNull(dataObject);
Assert.AreEqual("low", dataObject.DetailLevel);
Assert.AreEqual(1234, dataObject.UserId);
}
The only drawback is that I had to change DetailLevel from an enum to a string - if you keep the enum type in place, the DataContractJsonSerializer expects to read a numeric value and fails. See DataContractJsonSerializer and Enumsfor further details.
唯一的缺点是我必须将 DetailLevel 从枚举更改为字符串 - 如果您保留枚举类型,则 DataContractJsonSerializer 期望读取数值并失败。有关更多详细信息,请参阅DataContractJsonSerializer 和 Enums。
In my opinion this is quite poor, especially as JavaScriptSerializer handles it correctly. This is the exception that you get trying to parse a string into an enum:
在我看来,这很糟糕,尤其是当 JavaScriptSerializer 正确处理它时。这是您尝试将字符串解析为枚举的例外情况:
System.Runtime.Serialization.SerializationException: There was an error deserializing the object of type DataObject. The value 'low' cannot be parsed as the type 'Int64'. --->
System.Xml.XmlException: The value 'low' cannot be parsed as the type 'Int64'. --->
System.FormatException: Input string was not in a correct format
And marking up the enum like this does not change this behaviour:
像这样标记枚举不会改变这种行为:
[DataContract]
public enum DetailLevel
{
[EnumMember(Value = "low")]
Low,
...
}
This also seems to work in Silverlight.
这似乎也适用于 Silverlight。
回答by James Newton-King
Json.NETwill do what you want (disclaimer: I'm the author of the package). It supports reading DataContract/DataMember attributes as well as its own to change the property names. Also there is the StringEnumConverter class for serializing enum values as the name rather than the number.
Json.NET会做你想做的事(免责声明:我是这个包的作者)。它支持读取 DataContract/DataMember 属性以及它自己的属性来更改属性名称。还有 StringEnumConverter 类用于将枚举值序列化为名称而不是数字。
回答by Dan Appleyard
Create a class inherited from JavaScriptConverter. You must then implement three things:
创建一个继承自 JavaScriptConverter 的类。然后,您必须实现三件事:
Methods-
方法-
- Serialize
- Deserialize
- 连载
- 反序列化
Property-
财产-
- SupportedTypes
- 支持的类型
You can use the JavaScriptConverter class when you need more control over the serialization and deserialization process.
当您需要对序列化和反序列化过程进行更多控制时,可以使用 JavaScriptConverter 类。
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new MyCustomConverter() });
DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
回答by Paul Alexander
By creating a custom JavaScriptConverteryou can map any name to any property. But it does require hand coding the map, which is less than ideal.
通过创建自定义JavaScriptConverter,您可以将任何名称映射到任何属性。但它确实需要对地图进行手工编码,这不太理想。
public class DataObjectJavaScriptConverter : JavaScriptConverter
{
private static readonly Type[] _supportedTypes = new[]
{
typeof( DataObject )
};
public override IEnumerable<Type> SupportedTypes
{
get { return _supportedTypes; }
}
public override object Deserialize( IDictionary<string, object> dictionary,
Type type,
JavaScriptSerializer serializer )
{
if( type == typeof( DataObject ) )
{
var obj = new DataObject();
if( dictionary.ContainsKey( "user_id" ) )
obj.UserId = serializer.ConvertToType<int>(
dictionary["user_id"] );
if( dictionary.ContainsKey( "detail_level" ) )
obj.DetailLevel = serializer.ConvertToType<DetailLevel>(
dictionary["detail_level"] );
return obj;
}
return null;
}
public override IDictionary<string, object> Serialize(
object obj,
JavaScriptSerializer serializer )
{
var dataObj = obj as DataObject;
if( dataObj != null )
{
return new Dictionary<string,object>
{
{"user_id", dataObj.UserId },
{"detail_level", dataObj.DetailLevel }
}
}
return new Dictionary<string, object>();
}
}
Then you can deserialize like so:
然后你可以像这样反序列化:
var serializer = new JavaScriptSerializer();
serialzer.RegisterConverters( new[]{ new DataObjectJavaScriptConverter() } );
var dataObj = serializer.Deserialize<DataObject>( json );
回答by Vishvanatha Achary
I have used the using Newtonsoft.Json as below. Create an object:
我使用了下面的 Newtonsoft.Json。创建一个对象:
public class WorklistSortColumn
{
[JsonProperty(PropertyName = "field")]
public string Field { get; set; }
[JsonProperty(PropertyName = "dir")]
public string Direction { get; set; }
[JsonIgnore]
public string SortOrder { get; set; }
}
Now Call the below method to serialize to Json object as shown below.
现在调用下面的方法序列化为 Json 对象,如下所示。
string sortColumn = JsonConvert.SerializeObject(worklistSortColumn);
回答by Tom Maher
There is no standard support for renaming properties in JavaScriptSerializer
however you can quite easily add your own:
没有对重命名属性的标准支持,JavaScriptSerializer
但是您可以很容易地添加自己的属性:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.Reflection;
public class JsonConverter : JavaScriptConverter
{
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
List<MemberInfo> members = new List<MemberInfo>();
members.AddRange(type.GetFields());
members.AddRange(type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0));
object obj = Activator.CreateInstance(type);
foreach (MemberInfo member in members)
{
JsonPropertyAttribute jsonProperty = (JsonPropertyAttribute)Attribute.GetCustomAttribute(member, typeof(JsonPropertyAttribute));
if (jsonProperty != null && dictionary.ContainsKey(jsonProperty.Name))
{
SetMemberValue(serializer, member, obj, dictionary[jsonProperty.Name]);
}
else if (dictionary.ContainsKey(member.Name))
{
SetMemberValue(serializer, member, obj, dictionary[member.Name]);
}
else
{
KeyValuePair<string, object> kvp = dictionary.FirstOrDefault(x => string.Equals(x.Key, member.Name, StringComparison.InvariantCultureIgnoreCase));
if (!kvp.Equals(default(KeyValuePair<string, object>)))
{
SetMemberValue(serializer, member, obj, kvp.Value);
}
}
}
return obj;
}
private void SetMemberValue(JavaScriptSerializer serializer, MemberInfo member, object obj, object value)
{
if (member is PropertyInfo)
{
PropertyInfo property = (PropertyInfo)member;
property.SetValue(obj, serializer.ConvertToType(value, property.PropertyType), null);
}
else if (member is FieldInfo)
{
FieldInfo field = (FieldInfo)member;
field.SetValue(obj, serializer.ConvertToType(value, field.FieldType));
}
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Type type = obj.GetType();
List<MemberInfo> members = new List<MemberInfo>();
members.AddRange(type.GetFields());
members.AddRange(type.GetProperties().Where(p => p.CanRead && p.CanWrite && p.GetIndexParameters().Length == 0));
Dictionary<string, object> values = new Dictionary<string, object>();
foreach (MemberInfo member in members)
{
JsonPropertyAttribute jsonProperty = (JsonPropertyAttribute)Attribute.GetCustomAttribute(member, typeof(JsonPropertyAttribute));
if (jsonProperty != null)
{
values[jsonProperty.Name] = GetMemberValue(member, obj);
}
else
{
values[member.Name] = GetMemberValue(member, obj);
}
}
return values;
}
private object GetMemberValue(MemberInfo member, object obj)
{
if (member is PropertyInfo)
{
PropertyInfo property = (PropertyInfo)member;
return property.GetValue(obj, null);
}
else if (member is FieldInfo)
{
FieldInfo field = (FieldInfo)member;
return field.GetValue(obj);
}
return null;
}
public override IEnumerable<Type> SupportedTypes
{
get
{
return new[] { typeof(DataObject) };
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
public class JsonPropertyAttribute : Attribute
{
public JsonPropertyAttribute(string name)
{
Name = name;
}
public string Name
{
get;
set;
}
}
The DataObject
class then becomes:
然后DataObject
类变为:
public class DataObject
{
[JsonProperty("user_id")]
public int UserId { get; set; }
[JsonProperty("detail_level")]
public DetailLevel DetailLevel { get; set; }
}
I appreicate this might be a little late but thought other people wanting to use the JavaScriptSerializer
rather than the DataContractJsonSerializer
might appreciate it.
我很欣赏这可能有点晚了,但认为其他人想要使用JavaScriptSerializer
而不是DataContractJsonSerializer
可能会欣赏它。
回答by Ev.
My requirements included:
我的要求包括:
- must honor the dataContracts
- must deserialize dates in the format received in service
- must handle colelctions
- must target 3.5
- must NOT add an external dependency, especially not Newtonsoft (I'm creating a distributable package myself)
- must not be deserialized by hand
- 必须遵守数据合同
- 必须以服务中收到的格式反序列化日期
- 必须处理集合
- 必须以 3.5 为目标
- 不得添加外部依赖项,尤其是 Newtonsoft(我自己正在创建一个可分发包)
- 不得手动反序列化
My solution in the end was to use SimpleJson(https://github.com/facebook-csharp-sdk/simple-json).
我最终的解决方案是使用 SimpleJson( https://github.com/facebook-csharp-sdk/simple-json)。
Although you can install it via a nuget package, I included just that single SimpleJson.cs file (with the MIT license) in my project and referenced it.
尽管您可以通过 nuget 包安装它,但我只在我的项目中包含了那个 SimpleJson.cs 文件(带有 MIT 许可证)并引用了它。
I hope this helps someone.
我希望这可以帮助别人。
回答by Advait Purohit
For those who don't want to go for Newtonsoft Json.Netor DataContractJsonSerializer
for some reason (I can't think of any :) ), here is an implementation of JavaScriptConverter
that supports DataContract
and enum
to string
conversion -
对于那些谁不想去Newtonsoft Json.Net或DataContractJsonSerializer
出于某种原因(我想不出任何的:)),这里是一个实现JavaScriptConverter
支持DataContract
和enum
对string
转换-
public class DataContractJavaScriptConverter : JavaScriptConverter
{
private static readonly List<Type> _supportedTypes = new List<Type>();
static DataContractJavaScriptConverter()
{
foreach (Type type in Assembly.GetExecutingAssembly().DefinedTypes)
{
if (Attribute.IsDefined(type, typeof(DataContractAttribute)))
{
_supportedTypes.Add(type);
}
}
}
private bool ConvertEnumToString = false;
public DataContractJavaScriptConverter() : this(false)
{
}
public DataContractJavaScriptConverter(bool convertEnumToString)
{
ConvertEnumToString = convertEnumToString;
}
public override IEnumerable<Type> SupportedTypes
{
get { return _supportedTypes; }
}
public override object Deserialize(IDictionary<string, object> dictionary, Type type, JavaScriptSerializer serializer)
{
if (Attribute.IsDefined(type, typeof(DataContractAttribute)))
{
try
{
object instance = Activator.CreateInstance(type);
IEnumerable<MemberInfo> members = ((IEnumerable<MemberInfo>)type.GetFields())
.Concat(type.GetProperties().Where(property => property.CanWrite && property.GetIndexParameters().Length == 0))
.Where((member) => Attribute.IsDefined(member, typeof(DataMemberAttribute)));
foreach (MemberInfo member in members)
{
DataMemberAttribute attribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute));
object value;
if (dictionary.TryGetValue(attribute.Name, out value) == false)
{
if (attribute.IsRequired)
{
throw new SerializationException(String.Format("Required DataMember with name {0} not found", attribute.Name));
}
continue;
}
if (member.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)member;
object fieldValue;
if (ConvertEnumToString && field.FieldType.IsEnum)
{
fieldValue = Enum.Parse(field.FieldType, value.ToString());
}
else
{
fieldValue = serializer.ConvertToType(value, field.FieldType);
}
field.SetValue(instance, fieldValue);
}
else if (member.MemberType == MemberTypes.Property)
{
PropertyInfo property = (PropertyInfo)member;
object propertyValue;
if (ConvertEnumToString && property.PropertyType.IsEnum)
{
propertyValue = Enum.Parse(property.PropertyType, value.ToString());
}
else
{
propertyValue = serializer.ConvertToType(value, property.PropertyType);
}
property.SetValue(instance, propertyValue);
}
}
return instance;
}
catch (Exception)
{
return null;
}
}
return null;
}
public override IDictionary<string, object> Serialize(object obj, JavaScriptSerializer serializer)
{
Dictionary<string, object> dictionary = new Dictionary<string, object>();
if (obj != null && Attribute.IsDefined(obj.GetType(), typeof(DataContractAttribute)))
{
Type type = obj.GetType();
IEnumerable<MemberInfo> members = ((IEnumerable<MemberInfo>)type.GetFields())
.Concat(type.GetProperties().Where(property => property.CanRead && property.GetIndexParameters().Length == 0))
.Where((member) => Attribute.IsDefined(member, typeof(DataMemberAttribute)));
foreach (MemberInfo member in members)
{
DataMemberAttribute attribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute));
object value;
if (member.MemberType == MemberTypes.Field)
{
FieldInfo field = (FieldInfo)member;
if (ConvertEnumToString && field.FieldType.IsEnum)
{
value = field.GetValue(obj).ToString();
}
else
{
value = field.GetValue(obj);
}
}
else if (member.MemberType == MemberTypes.Property)
{
PropertyInfo property = (PropertyInfo)member;
if (ConvertEnumToString && property.PropertyType.IsEnum)
{
value = property.GetValue(obj).ToString();
}
else
{
value = property.GetValue(obj);
}
}
else
{
continue;
}
if (dictionary.ContainsKey(attribute.Name))
{
throw new SerializationException(String.Format("More than one DataMember found with name {0}", attribute.Name));
}
dictionary[attribute.Name] = value;
}
}
return dictionary;
}
}
Note: This DataContractJavaScriptConverter
will only handle DataContract
classes defined in the assembly where it is placed. If you want classes from separate assemblies, modify the _supportedTypes
list accordingly in the static constructror.
注意:这DataContractJavaScriptConverter
将只处理DataContract
在放置它的程序集中定义的类。如果您需要来自不同程序集的类,请_supportedTypes
在静态构造函数中相应地修改列表。
This can be used as follows -
这可以按如下方式使用 -
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new DataContractJavaScriptConverter(true) });
DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
The DataObject
class would look like this -
该DataObject
班是这样的-
using System.Runtime.Serialization;
[DataContract]
public class DataObject
{
[DataMember(Name = "user_id")]
public int UserId { get; set; }
[DataMember(Name = "detail_level")]
public string DetailLevel { get; set; }
}
Please note that this solution doesn't handle EmitDefaultValue
and Order
properties supported by DataMember
attribute.
请注意,此解决方案不处理EmitDefaultValue
,并Order
通过支持的属性DataMember
属性。