JavaScriptSerializer.Deserialize - 如何更改字段名称
摘要:如何使用JavaScriptSerializer.Deserialize将JSON数据中的字段名称映射到.Net对象的字段名称?JavaScriptSerializer.Deserialize - 如何更改字段名称
加长版:我有以下的JSON数据来我从一个服务器API(在.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是一个具有“低”作为值之一的枚举。
该测试失败:
[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中进行字段名称映射?它可以完成吗?
我又试了一次,使用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]等) – 2015-02-09 12:55:47
什么是你的JsonData?当我这样做,因为你已经写,我得到一个SerializationException,表明序列化程序正在期待一个根元素,就好像它期待XML一样。 我的JSON数据是{“user”:“THEDOMAIN \\ MDS”,“password”:“JJJJ”} – 2016-03-03 20:44:03
创建一个从JavaScriptConverter继承的类。然后,您必须实现三个方面:
方法 -
- 序列化
- 反序列化
属性 -
- SupportedTypes
当需要更多的控制序列化和反序列化过程时,可以使用JavaScriptConverter类。
JavaScriptSerializer serializer = new JavaScriptSerializer();
serializer.RegisterConverters(new JavaScriptConverter[] { new MyCustomConverter() });
DataObject dataObject = serializer.Deserialize<DataObject>(JsonData);
通过创建一个自定义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);
我用用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);
有在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; }
}
我appreicate这个威力有点晚,但认为其他人想要使用JavaScriptSerializer
而不是DataContractJsonSerializer
可能会感激。
我使用过类似JsonConverter
我的要求包括:
- 必须履行的dataContracts
- 必须反序列化日期在服务接收到的格式
- 必须处理colelctions
- 必须针对3.5
- 不能增加外部依赖关系,特别是不Newtonsoft(我自己创建一个可分发的软件包)
- must not手动解串
我最终的解决方案是使用SimpleJson(https://github.com/facebook-csharp-sdk/simple-json)。
虽然你可以通过nuget软件包进行安装,但是我在项目中只包含了一个SimpleJson.cs文件(带有MIT许可证)并引用了它。
我希望这可以帮助别人。
对于那些谁不想去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;
}
}
注意:此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
性质。
我讨厌`JavaScriptSerializer`。 `JwtSecurityTokenHandler`通过静态的`JsonExtensions.Serializer`属性来使用它,这意味着在运行时改变它可能会影响其他代码。不幸的是,这些类中的很多都是这种方式。 :( – NathanAldenSr 2016-07-19 19:42:32