Это немного некрасиво, но оказывается, что вы можете использовать IDataContractSurrogate
, чтобы десериализовать класс со свойством с переменным именем в 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"}
person
Brian Rogers
schedule
08.10.2015
Dictionary<string, object>
вместо строго типизированного класса. Будет ли это работать? - person Brian Rogers   schedule 08.10.2015Dictionary<string, string>
, но для этого вам нужно установитьDataContractJsonSerializerSettings.UseSimpleDictionaryFormat=true
, который доступен только в .Net 4.5 или более поздней версии. - person dbc   schedule 08.10.2015public string SpecialName { get; set; }
. - person dbc   schedule 09.10.2015