汇总:如何在使用JavaScriptSerializer.Deserialize时将JSON数据中的字段名映射到.Net对象的字段名?
更长版本的:下面的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是一个枚举,其中"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,因为我希望它在Silverlight之外工作。看起来XmlElement标签没有任何效果。我不知道我是从哪里得到他们的相关概念的,他们可能不是。
如何在JavaScriptSerializer中进行字段名映射?完全可以做到吗?
发布于 2009-07-10 05:43:07
我又试了一次,使用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和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
这样的标记并不能改变这种行为:
[DataContract]
public enum DetailLevel
{
[EnumMember(Value = "low")]
Low,
...
}
这似乎也适用于Silverlight。
发布于 2010-01-04 20:49:16
通过创建自定义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 );
发布于 2009-07-10 18:24:41
Json.NET会做你想做的事情(免责声明:我是软件包的作者)。它支持读取DataContract/DataMember属性以及它自己的属性来更改属性名。还有一个StringEnumConverter类,用于将枚举值序列化为名称而不是数字。
https://stackoverflow.com/questions/1100191
复制