JSON.NET解析为基于父类型的类型

时间:2017-03-18 20:08:47

标签: json.net

我通过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);
}

1 个答案:

答案 0 :(得分:0)

首先,您可以使用C# JSON.NET - Deserialize response that uses an unusual data structure中的ObjectToArrayConverter<T>非通用版本,将SettingASettingB类型与属性值数组进行序列化。最简单的方法是直接应用转换器为所有这些设置定义基类:

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

然后将SettingASettingB定义为派生类型:

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包含您感兴趣的设置的已定义属性,即SettingASettingB。标记为[JsonExtensionData]OtherSettings捕获了未知或不感兴趣的设置。

示例fiddle