是否可以使用.NET DataContractJSONSerializer反序列化格式不同于通常序列化的JSON文件?

时间:2016-12-20 00:55:29

标签: c# json datacontractjsonserializer

我有一个像这样格式化的JSON文件,它是由我无法编辑的工具生成的:

SELECT c.commisionAmount, s.studentName, p.paymentDate, p.paymentAmount,
       a2.username
FROM commisions c INNER JOIN
     accounts a
     ON c.commisionAccount = a.id INNER JOIN
     studentpayments sp
     ON c.paymentID = sp.id INNER JOIN
     students s
     ON p.paymentStudent = s.id LEFT JOIN
     accounts a2
     ON a2.id = s.signedupby
WHERE p.paymentDate BETWEEN '$startdate' AND '$enddate' AND
      a.id = '$id';

我正在尝试反序列化的类是这样的:

{
    "thing_name1": {
        "property1": 0,
        "property2": "sure"
    },
    "thing_name2": {
        "property1": 34,
        "property2": "absolutely"
    }
}

我需要将“thing_name1”和“thing_name2”的值放入其各自反序列化对象的ThingName数据成员中,但是如果不编写自定义(反)序列化程序,则无法找到一种简单的方法。或者编写一个快速的Python脚本来编写另一个文件,但这不会节省空间。

1 个答案:

答案 0 :(得分:1)

是的,这是可能的,但您确实需要一些自定义代码来执行此操作。

它有点难看,但你可以创建一个自定义IDataContractSurrogate类来将JSON反序列化为Dictionary<string, Dictionary<string, object>>,然后将嵌套字典结构中的值复制到{{1 }}。这是代理人需要的代码:

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设置,因此此解决方案仅适用于.Net 4.5或更高版本。

UseSimpleDictionaryFormat

请注意,在var settings = new DataContractJsonSerializerSettings(); settings.DataContractSurrogate = new MyDataContractSurrogate(); settings.KnownTypes = new List<Type> { typeof(Dictionary<string, Dictionary<string, object>>) }; settings.UseSimpleDictionaryFormat = true; 课程中,您不应将Thing成员标记为ThingName属性,因为它是在代理中专门处理的。另外,我假设您的类成员实际上是属性(带有[DataMember])而不是您在问题中写的字段。如果该假设不正确,您需要更改代理代码中{ get; set; }的所有引用,以使用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());
        }
    }
}