JSON 使用 DataContract 反序列化变量命名参数
JSON deserialization of variable named parameter using DataContract
假设我们有一个 JSON 对象类似于:
{
'12345': 'text string',
'rel': 'myResource'
}
构造一个 DataContract 来映射到这种类型似乎相当简单,例如:
[DataContract]
MyResource
{
[DataMember(Name = "12345")]
public string SpecialValue { get; set; }
[DataMember(Name = "rel")]
public string Rel { get; set; }
}
现在问题来了,属性 的名称是可变的,因此不能保证是“12345”。由于无法使用属性正确映射此变量,因此在使用 DataContractJsonSerializer 时不会获取它。
如果我更改 class 以支持 IExtensibleDataObject,我可以获得值部分但不是 属性 名称,这是一个问题。我希望在 deserialization/serialization 期间保持此值,以便能够根据 return 请求发送信息。我不打算改用 Json.NET 来解决这个问题,因为我想知道是否有可能以某种形式而不诉诸外部依赖。
有点难看,但事实证明您可以使用 IDataContractSurrogate
将变量名为 属性 的 class 反序列化为 Dictionary<string, object>
,然后复制字典中的值到你的class。当然,您需要在 class 中添加另一个 属性 来保存 "special" 属性 的名称。
这是我能够开始工作的代理示例:
class MyDataContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type == typeof(MyResource))
{
return typeof(Dictionary<string, object>);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj.GetType() == typeof(Dictionary<string, object>) &&
targetType == typeof(MyResource))
{
Dictionary<string, object> dict = (Dictionary<string, object>)obj;
MyResource mr = new MyResource();
foreach (PropertyInfo prop in GetInterestingProperties(typeof(MyResource)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
object value;
if (dict.TryGetValue(att.Name, out value))
{
prop.SetValue(mr, value);
dict.Remove(att.Name);
}
}
// should only be one property left in the dictionary
if (dict.Count > 0)
{
var kvp = dict.First();
mr.SpecialName = kvp.Key;
mr.SpecialValue = (string)kvp.Value;
}
return mr;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(MyResource) &&
targetType == typeof(Dictionary<string, object>))
{
MyResource mr = (MyResource)obj;
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add(mr.SpecialName, mr.SpecialValue);
foreach (PropertyInfo prop in GetInterestingProperties(typeof(MyResource)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
dict.Add(att.Name, prop.GetValue(mr));
}
return dict;
}
return obj;
}
private IEnumerable<PropertyInfo> GetInterestingProperties(Type type)
{
return type.GetProperties().Where(p => p.CanRead && p.CanWrite &&
p.GetCustomAttribute<DataMemberAttribute>() != null);
}
// ------- The rest of these methods are not needed -------
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
}
要使用代理项,您需要创建 DataContractJsonSerializerSettings
的实例并将其传递给具有以下属性集的 DataContractJsonSerializer
。请注意,由于我们需要 UseSimpleDictionaryFormat
设置,因此此解决方案仅适用于 .Net 4.5 或更高版本。
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, object>) };
settings.UseSimpleDictionaryFormat = true;
另请注意,在您的 class 中,您不应使用 [DataMember]
属性标记 "special" 属性,因为它们在代理项中进行了特殊处理。
[DataContract]
class MyResource
{
// Don't mark these with [DataMember]
public string SpecialName { get; set; }
public string SpecialValue { get; set; }
[DataMember(Name = "rel")]
public string Rel { get; set; }
}
这是一个演示:
class Program
{
static void Main(string[] args)
{
string json = @"
{
""12345"": ""text string"",
""rel"": ""myResource""
}";
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, object>) };
settings.UseSimpleDictionaryFormat = true;
MyResource mr = Deserialize<MyResource>(json, settings);
Console.WriteLine("Special name: " + mr.SpecialName);
Console.WriteLine("Special value: " + mr.SpecialValue);
Console.WriteLine("Rel: " + mr.Rel);
Console.WriteLine();
json = Serialize(mr, settings);
Console.WriteLine(json);
}
public static T Deserialize<T>(string json, DataContractJsonSerializerSettings settings = null)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
if (settings == null) settings = GetDefaultSerializerSettings();
var ser = new DataContractJsonSerializer(typeof(T), settings);
return (T)ser.ReadObject(ms);
}
}
public static string Serialize(object obj, DataContractJsonSerializerSettings settings = null)
{
using (MemoryStream ms = new MemoryStream())
{
if (settings == null) settings = GetDefaultSerializerSettings();
var ser = new DataContractJsonSerializer(obj.GetType(), settings);
ser.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
输出:
Special name: 12345
Special value: text string
Rel: myResource
{"12345":"text string","rel":"myResource"}
假设我们有一个 JSON 对象类似于:
{
'12345': 'text string',
'rel': 'myResource'
}
构造一个 DataContract 来映射到这种类型似乎相当简单,例如:
[DataContract]
MyResource
{
[DataMember(Name = "12345")]
public string SpecialValue { get; set; }
[DataMember(Name = "rel")]
public string Rel { get; set; }
}
现在问题来了,属性 的名称是可变的,因此不能保证是“12345”。由于无法使用属性正确映射此变量,因此在使用 DataContractJsonSerializer 时不会获取它。
如果我更改 class 以支持 IExtensibleDataObject,我可以获得值部分但不是 属性 名称,这是一个问题。我希望在 deserialization/serialization 期间保持此值,以便能够根据 return 请求发送信息。我不打算改用 Json.NET 来解决这个问题,因为我想知道是否有可能以某种形式而不诉诸外部依赖。
有点难看,但事实证明您可以使用 IDataContractSurrogate
将变量名为 属性 的 class 反序列化为 Dictionary<string, object>
,然后复制字典中的值到你的class。当然,您需要在 class 中添加另一个 属性 来保存 "special" 属性 的名称。
这是我能够开始工作的代理示例:
class MyDataContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type == typeof(MyResource))
{
return typeof(Dictionary<string, object>);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj.GetType() == typeof(Dictionary<string, object>) &&
targetType == typeof(MyResource))
{
Dictionary<string, object> dict = (Dictionary<string, object>)obj;
MyResource mr = new MyResource();
foreach (PropertyInfo prop in GetInterestingProperties(typeof(MyResource)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
object value;
if (dict.TryGetValue(att.Name, out value))
{
prop.SetValue(mr, value);
dict.Remove(att.Name);
}
}
// should only be one property left in the dictionary
if (dict.Count > 0)
{
var kvp = dict.First();
mr.SpecialName = kvp.Key;
mr.SpecialValue = (string)kvp.Value;
}
return mr;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(MyResource) &&
targetType == typeof(Dictionary<string, object>))
{
MyResource mr = (MyResource)obj;
Dictionary<string, object> dict = new Dictionary<string, object>();
dict.Add(mr.SpecialName, mr.SpecialValue);
foreach (PropertyInfo prop in GetInterestingProperties(typeof(MyResource)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
dict.Add(att.Name, prop.GetValue(mr));
}
return dict;
}
return obj;
}
private IEnumerable<PropertyInfo> GetInterestingProperties(Type type)
{
return type.GetProperties().Where(p => p.CanRead && p.CanWrite &&
p.GetCustomAttribute<DataMemberAttribute>() != null);
}
// ------- The rest of these methods are not needed -------
public object GetCustomDataToExport(Type clrType, Type dataContractType)
{
throw new NotImplementedException();
}
public object GetCustomDataToExport(MemberInfo memberInfo, Type dataContractType)
{
throw new NotImplementedException();
}
public void GetKnownCustomDataTypes(System.Collections.ObjectModel.Collection<Type> customDataTypes)
{
throw new NotImplementedException();
}
public Type GetReferencedTypeOnImport(string typeName, string typeNamespace, object customData)
{
throw new NotImplementedException();
}
public System.CodeDom.CodeTypeDeclaration ProcessImportedType(System.CodeDom.CodeTypeDeclaration typeDeclaration, System.CodeDom.CodeCompileUnit compileUnit)
{
throw new NotImplementedException();
}
}
要使用代理项,您需要创建 DataContractJsonSerializerSettings
的实例并将其传递给具有以下属性集的 DataContractJsonSerializer
。请注意,由于我们需要 UseSimpleDictionaryFormat
设置,因此此解决方案仅适用于 .Net 4.5 或更高版本。
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, object>) };
settings.UseSimpleDictionaryFormat = true;
另请注意,在您的 class 中,您不应使用 [DataMember]
属性标记 "special" 属性,因为它们在代理项中进行了特殊处理。
[DataContract]
class MyResource
{
// Don't mark these with [DataMember]
public string SpecialName { get; set; }
public string SpecialValue { get; set; }
[DataMember(Name = "rel")]
public string Rel { get; set; }
}
这是一个演示:
class Program
{
static void Main(string[] args)
{
string json = @"
{
""12345"": ""text string"",
""rel"": ""myResource""
}";
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, object>) };
settings.UseSimpleDictionaryFormat = true;
MyResource mr = Deserialize<MyResource>(json, settings);
Console.WriteLine("Special name: " + mr.SpecialName);
Console.WriteLine("Special value: " + mr.SpecialValue);
Console.WriteLine("Rel: " + mr.Rel);
Console.WriteLine();
json = Serialize(mr, settings);
Console.WriteLine(json);
}
public static T Deserialize<T>(string json, DataContractJsonSerializerSettings settings = null)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
if (settings == null) settings = GetDefaultSerializerSettings();
var ser = new DataContractJsonSerializer(typeof(T), settings);
return (T)ser.ReadObject(ms);
}
}
public static string Serialize(object obj, DataContractJsonSerializerSettings settings = null)
{
using (MemoryStream ms = new MemoryStream())
{
if (settings == null) settings = GetDefaultSerializerSettings();
var ser = new DataContractJsonSerializer(obj.GetType(), settings);
ser.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
输出:
Special name: 12345
Special value: text string
Rel: myResource
{"12345":"text string","rel":"myResource"}