是否可以使用 .NET DataContractJSONSerializer 反序列化格式不同于通常序列化的 JSON 文件?
Is it possible to use the .NET DataContractJSONSerializer to deserialize a JSON file formatted differently than it would usually serialize?
我有一个 JSON 格式的文件,它是由我无法编辑的工具生成的:
{
"thing_name1": {
"property1": 0,
"property2": "sure"
},
"thing_name2": {
"property1": 34,
"property2": "absolutely"
}
}
我正在尝试反序列化的 class 是这样的:
[DataContract]
public class Thing
{
[DataMember]
public String ThingName;
[DataMember(Name="property1")]
public int Property1;
[DataMember(Name="property2")]
public String Property2;
}
我需要将 "thing_name1" 和 "thing_name2" 的值放入它们各自的反序列化对象的 ThingName 数据成员中,但一直无法找到一种不编写代码的简单方法自定义(反)序列化器。或者编写一个快速 Python 脚本来编写另一个文件,但这不会很 space 有效。
是的,这是可能的,但您确实需要一些自定义代码才能做到这一点。
有点难看,但是你可以创建一个自定义的IDataContractSurrogate
class来将JSON反序列化为Dictionary<string, Dictionary<string, object>>
,然后从嵌套的值中复制值字典结构变成了List<Thing>
。这是代理人需要的代码:
class MyDataContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type == typeof(List<Thing>))
{
return typeof(Dictionary<string, Dictionary<string, object>>);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj.GetType() == typeof(Dictionary<string, Dictionary<string, object>>) &&
targetType == typeof(List<Thing>))
{
List<Thing> list = new List<Thing>();
foreach (var kvp in (Dictionary<string, Dictionary<string, object>>)obj)
{
Thing thing = new Thing { ThingName = kvp.Key };
Dictionary<string, object> propsDict = kvp.Value;
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(Thing)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
object value;
if (propsDict.TryGetValue(att.Name, out value))
{
prop.SetValue(thing, value);
}
}
list.Add(thing);
}
return list;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(List<Thing>) &&
targetType == typeof(Dictionary<string, Dictionary<string, object>>))
{
var thingsDict = new Dictionary<string, Dictionary<string, object>>();
foreach (Thing thing in (List<Thing>)obj)
{
var propsDict = new Dictionary<string, object>();
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(Thing)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
propsDict.Add(att.Name, prop.GetValue(thing));
}
thingsDict.Add(thing.ThingName, propsDict);
}
return thingsDict;
}
return obj;
}
private IEnumerable<PropertyInfo> GetDataMemberProperties(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, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
请注意,在您的 Thing
class 中,您不应使用 [DataMember]
属性标记 ThingName
成员,因为它在代理中进行了特殊处理。此外,我假设您的 class 成员实际上是 properties(带有 { get; set; }
),而不是像您这样的 fields在你的问题中写道。如果该假设不正确,您需要将代理代码中对 PropertyInfo
的所有引用更改为使用 FieldInfo
;否则代理将无法工作。
[DataContract]
public class Thing
{
// Don't mark this property with [DataMember]
public string ThingName { get; set; }
[DataMember(Name = "property1")]
public int Property1 { get; set; }
[DataMember(Name = "property2")]
public string Property2 { get; set; }
}
这是一个往返演示:
public class Program
{
public static void Main(string[] args)
{
string json = @"
{
""thing_name1"": {
""property1"": 0,
""property2"": ""sure""
},
""thing_name2"": {
""property1"": 34,
""property2"": ""absolutely""
}
}";
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
List<Thing> things = Deserialize<List<Thing>>(json, settings);
foreach (Thing thing in things)
{
Console.WriteLine("ThingName: " + thing.ThingName);
Console.WriteLine("Property1: " + thing.Property1);
Console.WriteLine("Property2: " + thing.Property2);
Console.WriteLine();
}
json = Serialize(things, settings);
Console.WriteLine(json);
}
public static T Deserialize<T>(string json, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(typeof(T), settings);
return (T)ser.ReadObject(ms);
}
}
public static string Serialize(object obj, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream())
{
var ser = new DataContractJsonSerializer(obj.GetType(), settings);
ser.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
输出:
ThingName: thing_name1
Property1: 0
Property2: sure
ThingName: thing_name2
Property1: 34
Property2: absolutely
{"thing_name1":{"property1":0,"property2":"sure"},"thing_name2":{"property1":34,"property2":"absolutely"}}
我有一个 JSON 格式的文件,它是由我无法编辑的工具生成的:
{
"thing_name1": {
"property1": 0,
"property2": "sure"
},
"thing_name2": {
"property1": 34,
"property2": "absolutely"
}
}
我正在尝试反序列化的 class 是这样的:
[DataContract]
public class Thing
{
[DataMember]
public String ThingName;
[DataMember(Name="property1")]
public int Property1;
[DataMember(Name="property2")]
public String Property2;
}
我需要将 "thing_name1" 和 "thing_name2" 的值放入它们各自的反序列化对象的 ThingName 数据成员中,但一直无法找到一种不编写代码的简单方法自定义(反)序列化器。或者编写一个快速 Python 脚本来编写另一个文件,但这不会很 space 有效。
是的,这是可能的,但您确实需要一些自定义代码才能做到这一点。
有点难看,但是你可以创建一个自定义的IDataContractSurrogate
class来将JSON反序列化为Dictionary<string, Dictionary<string, object>>
,然后从嵌套的值中复制值字典结构变成了List<Thing>
。这是代理人需要的代码:
class MyDataContractSurrogate : IDataContractSurrogate
{
public Type GetDataContractType(Type type)
{
if (type == typeof(List<Thing>))
{
return typeof(Dictionary<string, Dictionary<string, object>>);
}
return type;
}
public object GetDeserializedObject(object obj, Type targetType)
{
if (obj.GetType() == typeof(Dictionary<string, Dictionary<string, object>>) &&
targetType == typeof(List<Thing>))
{
List<Thing> list = new List<Thing>();
foreach (var kvp in (Dictionary<string, Dictionary<string, object>>)obj)
{
Thing thing = new Thing { ThingName = kvp.Key };
Dictionary<string, object> propsDict = kvp.Value;
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(Thing)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
object value;
if (propsDict.TryGetValue(att.Name, out value))
{
prop.SetValue(thing, value);
}
}
list.Add(thing);
}
return list;
}
return obj;
}
public object GetObjectToSerialize(object obj, Type targetType)
{
if (obj.GetType() == typeof(List<Thing>) &&
targetType == typeof(Dictionary<string, Dictionary<string, object>>))
{
var thingsDict = new Dictionary<string, Dictionary<string, object>>();
foreach (Thing thing in (List<Thing>)obj)
{
var propsDict = new Dictionary<string, object>();
foreach (PropertyInfo prop in GetDataMemberProperties(typeof(Thing)))
{
DataMemberAttribute att = prop.GetCustomAttribute<DataMemberAttribute>();
propsDict.Add(att.Name, prop.GetValue(thing));
}
thingsDict.Add(thing.ThingName, propsDict);
}
return thingsDict;
}
return obj;
}
private IEnumerable<PropertyInfo> GetDataMemberProperties(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, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
请注意,在您的 Thing
class 中,您不应使用 [DataMember]
属性标记 ThingName
成员,因为它在代理中进行了特殊处理。此外,我假设您的 class 成员实际上是 properties(带有 { get; set; }
),而不是像您这样的 fields在你的问题中写道。如果该假设不正确,您需要将代理代码中对 PropertyInfo
的所有引用更改为使用 FieldInfo
;否则代理将无法工作。
[DataContract]
public class Thing
{
// Don't mark this property with [DataMember]
public string ThingName { get; set; }
[DataMember(Name = "property1")]
public int Property1 { get; set; }
[DataMember(Name = "property2")]
public string Property2 { get; set; }
}
这是一个往返演示:
public class Program
{
public static void Main(string[] args)
{
string json = @"
{
""thing_name1"": {
""property1"": 0,
""property2"": ""sure""
},
""thing_name2"": {
""property1"": 34,
""property2"": ""absolutely""
}
}";
var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) };
settings.UseSimpleDictionaryFormat = true;
List<Thing> things = Deserialize<List<Thing>>(json, settings);
foreach (Thing thing in things)
{
Console.WriteLine("ThingName: " + thing.ThingName);
Console.WriteLine("Property1: " + thing.Property1);
Console.WriteLine("Property2: " + thing.Property2);
Console.WriteLine();
}
json = Serialize(things, settings);
Console.WriteLine(json);
}
public static T Deserialize<T>(string json, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
var ser = new DataContractJsonSerializer(typeof(T), settings);
return (T)ser.ReadObject(ms);
}
}
public static string Serialize(object obj, DataContractJsonSerializerSettings settings)
{
using (MemoryStream ms = new MemoryStream())
{
var ser = new DataContractJsonSerializer(obj.GetType(), settings);
ser.WriteObject(ms, obj);
return Encoding.UTF8.GetString(ms.ToArray());
}
}
}
输出:
ThingName: thing_name1
Property1: 0
Property2: sure
ThingName: thing_name2
Property1: 34
Property2: absolutely
{"thing_name1":{"property1":0,"property2":"sure"},"thing_name2":{"property1":34,"property2":"absolutely"}}