1

假设我们有一个类似于以下内容的 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 时不会被拾取。

如果我将类更改为支持 IExtensibleDataObject,我可以获得值部分,但不能获得有问题的属性名称。我希望在反序列化/序列化期间保持此值,以便能够在退货请求中发送信息。我不打算改用 Json.NET 来解决这个问题,因为我想知道是否可以在不依赖外部依赖的情况下以某种形式出现。

4

1 回答 1

2

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

这是我能够开始工作的示例代理:

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;

另请注意,在您的类中,您不应使用属性标记“特殊”[DataMember]属性,因为它们在代理中被特别处理。

[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"}
于 2015-10-08T23:21:46.350 回答