使用DataContract对名为parameter的变量进行JSON反序列化 [英] JSON deserialization of variable named parameter using DataContract

查看:85
本文介绍了使用DataContract对名为parameter的变量进行JSON反序列化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我们有一个类似于JSON对象:

Assuming we have a JSON object similar to:

{
  '12345': 'text string',
  'rel': 'myResource'
}

构建要映射的数据合同这种类型似乎很简单,例如:

Constructing a DataContract to map to this type seems fairly simple such as:

[DataContract]
MyResource
{
  [DataMember(Name = "12345")]
  public string SpecialValue { get; set; }

  [DataMember(Name = "rel")]
  public string Rel { get; set; }
}

现在问题来了,属性名称是可变的,所以它不是保证为 12345。由于无法使用属性正确映射此变量,因此在使用DataContractJsonSerializer时不会将其拾取。

Now the problem arrives that the name of the property is variable so it not guaranteed to be '12345'. Since this variable cannot be properly mapped using attributes it won't get picked up when using DataContractJsonSerializer.

如果我更改类以支持IExtensibleDataObject,则可以获取值部分但是属性名称不是问题。我希望在反序列化/序列化期间保持该值,以便能够在退货请求中发送信息。我不想切换到使用Json.NET来解决此问题,因为我想知道是否有可能以某种形式使用而不依赖外部依赖项。

If I change the class to support IExtensibleDataObject, I can get the value portion but not the property name which is a problem. I'm looking to maintain this value during deserialization/serialization in order to be able to send the information on a return request. I'm not looking to change over to using Json.NET to solve this problem as I want to know if it is possible in some form without resorting to an external dependency.

推荐答案

这有点难看,但事实证明您可以使用 IDataContractSurrogate 将具有可变名称属性的类反序列化为 Dictionary< string,object> ,然后将字典中的值复制到您的类中。当然,您将需要在类中添加另一个属性来保存特殊属性的名称。

It's a little ugly, but it turns out you can use an IDataContractSurrogate to deserialize the class with the variably named property into a Dictionary<string, object> and then copy the values from the dictionary into your class. Of course, you will need to add another property to your class to hold the name of the "special" property.

以下是我能够上班的替代示例:

Here is an example surrogate that I was able to get working:

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或更高版本。

To use the surrogate, you'll need to create an instance of DataContractJsonSerializerSettings and pass it to the DataContractJsonSerializer with the following properties set. Note that since we require the UseSimpleDictionaryFormat setting, this solution will only work with .Net 4.5 or later.

var settings = new DataContractJsonSerializerSettings();
settings.DataContractSurrogate = new MyDataContractSurrogate();
settings.KnownTypes = new List<Type> { typeof(Dictionary<string, object>) };
settings.UseSimpleDictionaryFormat = true;

还请注意,在您的课程中,请勿使用<$ c $标记特殊属性c> [DataMember] 属性,因为它们是在代理中专门处理的。

Note also that in your class you should NOT mark the "special" properties with a [DataMember] attribute, since they are handled specially in the surrogate.

[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"}

这篇关于使用DataContract对名为parameter的变量进行JSON反序列化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆