JavaScriptSerializer.Deserialize - 如何更改字段名称
Posted
技术标签:
【中文标题】JavaScriptSerializer.Deserialize - 如何更改字段名称【英文标题】:JavaScriptSerializer.Deserialize - how to change field names 【发布时间】:2010-11-09 04:03:02 【问题描述】:总结:使用 javascriptSerializer.Deserialize 时,如何将 JSON 数据中的字段名称映射到 .Net 对象的字段名称?
加长版:我从服务器 API 收到以下 JSON 数据(未在 .Net 中编码)
"user_id":1234, "detail_level":"low"
我有以下 C# 对象:
[Serializable]
public class DataObject
[XmlElement("user_id")]
public int UserId get; set;
[XmlElement("detail_level")]
public DetailLevel DetailLevel get; set;
其中 DetailLevel 是一个以“Low”作为值之一的枚举。
此测试失败:
[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);
最后两个断言失败,因为这些字段中没有数据。如果我将 JSON 数据更改为
"userid":1234, "detaillevel":"low"
然后它通过了。但我无法更改服务器的行为,并且我希望客户端类在 C# 习语中具有命名良好的属性。我不能使用 LINQ to JSON,因为我希望它在 Silverlight 之外工作。看起来 XmlElement 标签没有效果。我完全不知道我从哪里得到它们相关的想法,它们可能不相关。
如何在 JavaScriptSerializer 中进行字段名映射?能做到吗?
【问题讨论】:
我讨厌JavaScriptSerializer
。 JwtSecurityTokenHandler
通过静态 JsonExtensions.Serializer
属性使用它,这意味着在运行时更改它可能会影响其他期望它不变的代码。不幸的是,其中许多课程都是这样的。 :(
【参考方案1】:
我又尝试了一次,使用DataContractJsonSerializer 类。这样就解决了:
代码如下所示:
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;
测试是:
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);
唯一的缺点是我必须将 DetailLevel 从枚举更改为字符串 - 如果将枚举类型保留在适当的位置,DataContractJsonSerializer 预计会读取数值并失败。有关详细信息,请参阅DataContractJsonSerializer and Enums。
在我看来,这很糟糕,尤其是当 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
像这样标记枚举不会改变这种行为:
[DataContract]
public enum DetailLevel
[EnumMember(Value = "low")]
Low,
...
这似乎也适用于 Silverlight。
【讨论】:
很好的解决方案!使用 .Net 4.5 似乎对于仅具有简单 [DataMember] 声明的枚举成员(不需要 [EnumMember] 等)工作正常 您的 JsonData 中有什么?当我按照您所写的那样执行此操作时,我得到一个 SerializationException ,其大意是 Serializer 期待一个根元素,就好像它期待 XML 一样。我的 JSON 数据是 "user": "THEDOMAIN\\MDS", "password": "JJJJ"【参考方案2】:通过创建自定义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>();
然后你可以像这样反序列化:
var serializer = new JavaScriptSerializer();
serialzer.RegisterConverters( new[] new DataObjectJavaScriptConverter() );
var dataObj = serializer.Deserialize<DataObject>( json );
【讨论】:
【参考方案3】:Json.NET 会做你想做的事(免责声明:我是包的作者)。它支持读取 DataContract/DataMember 属性以及它自己的属性来更改属性名称。还有 StringEnumConverter 类用于将枚举值序列化为名称而不是数字。
【讨论】:
一个两行代码示例显示使用该属性会很高兴在这个答案中看到。【参考方案4】: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;
DataObject
类则变为:
public class DataObject
[JsonProperty("user_id")]
public int UserId get; set;
[JsonProperty("detail_level")]
public DetailLevel DetailLevel get; set;
我知道这可能有点晚了,但我认为其他想要使用 JavaScriptSerializer
而不是 DataContractJsonSerializer
的人可能会喜欢它。
【讨论】:
我已将您的代码与 JsonConverter创建一个继承自 JavaScriptConverter 的类。然后你必须实现三件事:
方法-
-
序列化
反序列化
物业-
-
支持的类型
当您需要对序列化和反序列化过程进行更多控制时,可以使用 JavaScriptConverter 类。
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] new MyCustomConverter() );
DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
Here is a link for further information
【讨论】:
【参考方案6】:我已经使用了下面的使用 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;
现在调用下面的方法序列化为 Json 对象,如下所示。
string sortColumn = JsonConvert.SerializeObject(worklistSortColumn);
【讨论】:
【参考方案7】:对于那些出于某种原因不想使用Newtonsoft Json.Net 或DataContractJsonSerializer
的人(我想不出任何:)),这是支持DataContract
和@ 的JavaScriptConverter
的实现987654326@到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;
注意:这个DataContractJavaScriptConverter
将只处理在它所在的程序集中定义的DataContract
类。如果您想要来自不同程序集的类,请在静态构造函数中相应地修改_supportedTypes
列表。
这可以如下使用-
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] new DataContractJavaScriptConverter(true) );
DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
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;
请注意,此解决方案不处理 DataMember
属性支持的 EmitDefaultValue
和 Order
属性。
【讨论】:
ASP.NET 在将客户端 json(通过 ajax 到 WebMethod)转换为 .NET 对象时使用JavascriptSerializer
,因此像我这样在网站上工作的想要处理客户端数据的用户别无选择使用微软的版本。对于序列化,我们当然可以使用 Newtonsoft,但是反序列化需要这个。来源:referencesource.microsoft.com/#System.Web.Extensions/Script/…【参考方案8】:
我的要求包括:
必须遵守数据合同 必须以服务中收到的格式反序列化日期 必须处理集合 必须以 3.5 为目标 不得添加外部依赖项,尤其是 Newtonsoft(我正在自己创建可分发包) 不得手动反序列化我最终的解决方案是使用 SimpleJson(https://github.com/facebook-csharp-sdk/simple-json)。
虽然您可以通过 nuget 包安装它,但我在项目中只包含了一个 SimpleJson.cs 文件(使用 MIT 许可证)并引用了它。
我希望这对某人有所帮助。
【讨论】:
以上是关于JavaScriptSerializer.Deserialize - 如何更改字段名称的主要内容,如果未能解决你的问题,请参考以下文章