我通过JSON从API获得了一大堆“设置”,如下所示:
{
...
"SettingA": { "value": [
[ 3200, 0 ],
[ 4300, 0 ],
[ 5600, 0 ],
[ 7000, 0 ]
], "type": "array", "readonly": 1},
"SettingB": { "value": [
[ 3, 3320, -0.6, "Auto WB" ],
[ 7, 3200, 0, "Tungsten" ],
[ 7, 4300, 0, "Fluorescent" ],
[ 7, 5600, 0, "Daylight" ],
[ 7, 7000, 0, "Daylight cool" ]
], "type": "array", "readonly": 1}
...
}
基于像“SettingA”或“SettingB”之类的关键字,我需要将它的VALUE属性序列化为特定类型(如果已知,某些设置可以保留为JTOKEN或JARRAY,因为我不喜欢不关心)
是否有一种将VALUE反序列化为相应类型的优雅方式?特别是,因为属性没有命名,所以我必须通过索引分配属性。
目前,我这样做(有效,但需要大量手写):
foreach (JToken item in table)
{
var p = new WhiteBalancePreset(item.Value<int>(0), item.Value<double>(1),
item.Value<double>(2), item.Value<string>(3));
if (p.State != 0)
presets.Add(p);
}
答案 0 :(得分:0)
首先,您可以使用C# JSON.NET - Deserialize response that uses an unusual data structure中的ObjectToArrayConverter<T>
非通用版本,将SettingA
和SettingB
类型与属性值数组进行序列化。最简单的方法是直接应用转换器为所有这些设置定义基类:
[JsonConverter(typeof(ObjectToArrayConverter))]
public abstract class SettingBase
{
}
public class ObjectToArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
static bool ShouldSkip(JsonProperty property)
{
return property.Ignored || !property.Readable || !property.Writable;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var type = value.GetType();
var contract = serializer.ContractResolver.ResolveContract(type) as JsonObjectContract;
if (contract == null)
throw new JsonSerializationException("invalid type " + type.FullName);
var list = contract.Properties.Where(p => !ShouldSkip(p)).Select(p => p.ValueProvider.GetValue(value));
serializer.Serialize(writer, list);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var token = JToken.Load(reader);
if (token.Type != JTokenType.Array)
throw new JsonSerializationException("token was not an array");
var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract;
if (contract == null)
throw new JsonSerializationException("invalid type " + objectType.FullName);
var value = existingValue ?? contract.DefaultCreator();
foreach (var pair in contract.Properties.Where(p => !ShouldSkip(p)).Zip(token, (p, v) => new { Value = v, Property = p }))
{
var propertyValue = pair.Value.ToObject(pair.Property.PropertyType, serializer);
pair.Property.ValueProvider.SetValue(value, propertyValue);
}
return value;
}
}
然后将SettingA
和SettingB
定义为派生类型:
public class SettingA : SettingBase
{
[JsonProperty(Order = 1)]
public int Property1 { get; set; }
[JsonProperty(Order = 2)]
public double Property2 { get; set; }
}
public class SettingB : SettingBase
{
[JsonProperty(Order = 1)]
public int Code { get; set; }
[JsonProperty(Order = 2)]
public double ColorTemperature { get; set; }
[JsonProperty(Order = 3)]
public double GammaCorrection { get; set; }
[JsonProperty(Order = 4)]
public string Name { get; set; }
}
现在,它们将作为属性值的数组而不是对象从JSON序列化。请注意,必须使用此转换器可靠地序列化所有属性[JsonProperty(Order = XXX)]
。可以使用[DataContract]
和[DataMember(Order = XXX)]
,因为它们也是supported by Newtonsoft。
下一个阶段,正确反序列化命名的JSON属性"SettingA"
和"SettingB"
,也可以使用泛型来处理。定义以下类型:
public class SettingsTable<TSettings> where TSettings : SettingBase
{
public SettingsTable() { this.Value = new List<TSettings>(); }
[JsonProperty("value")]
public List<TSettings> Value { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("readonly")]
public int Readonly { get; set; }
}
public class SettingsSet
{
public SettingsSet() { this.OtherSettings = new Dictionary<string, JToken>(); }
public SettingsTable<SettingA> SettingA { get; set; }
public SettingsTable<SettingB> SettingB { get; set; }
[JsonExtensionData]
public Dictionary<string, JToken> OtherSettings { get; set; }
}
根类型SettingsSet
包含您感兴趣的设置的已定义属性,即SettingA
和SettingB
。标记为[JsonExtensionData]
的OtherSettings
捕获了未知或不感兴趣的设置。
示例fiddle。