如何将具有固定架构的值数组反序列化为强类型数据类? [英] How to deserialize an array of values with a fixed schema to a strongly typed data class?

查看:14
本文介绍了如何将具有固定架构的值数组反序列化为强类型数据类?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在想出一种干净(尽可能)的方式来反序列化某些特定格式的 JSON 数据时遇到了一些麻烦.我想将数据反序列化为强类型数据对象类,这在细节方面非常灵活.以下是数据外观的示例:

I am having some trouble figuring out a clean (as possible) way to deserialize some JSON data in a particular format. I want to deserialize the data to strongly typed data object classes, pretty flexible regarding the specifics of this. Here is an example of what the data looks like:

{
    "timestamp": 1473730993,
    "total_players": 945,
    "max_score": 8961474,
    "players": {
            "Player1Username": [
            121,
            "somestring",
            679900,
            5,
            4497,
            "anotherString",
            "thirdString",
            "fourthString",
            123,
            22,
            "YetAnotherString"],
        "Player2Username": [
            886,
            "stillAstring",
            1677,
            1,
            9876,
            "alwaysAstring",
            "thirdString",
            "fourthString",
            876,
            77,
            "string"]
        }
}

我不确定的具体部分是:

The specific parts I am unsure about are:

  1. 玩家的合集会被认为是一本字典吗?用户名可以用作键,但值让我望而却步,因为它是字符串和整数值的混合集合.
  2. 玩家完全由未命名的值组成.我几乎一直使用具有命名属性和值的 JSON 数据(例如时间戳、total_players 等在最顶部)

假设我有一个这样的顶级课程:

Say I have a top level class like this:

public class ScoreboardResults
{
    public int timestamp { get; set; }
    public int total_players { get; set; }
    public int max_score { get; set; }
    public List<Player> players { get; set; }
}

如果 Player 对象基本上是一个以用户名作为键的键/值,并且该值是一个混合整数和字符串的集合,那么它会是什么样子?每个玩家元素的数据总是以相同的顺序排列,所以我知道集合中的第一个值是他们的 UniqueID,第二个值是玩家描述等.我希望玩家类是这样的:

What would the Player object look like given that it is basically a key/value with the username serving as the key, and the value being a collection of mixed integers and strings? The data for each player element is always in the same order, so I know that the first value in the collection is their UniqueID, the second value is a player description, etc. I would like the player class to be something like this:

public class Player
{
    public string Username { get; set; }
    public int UniqueID { get; set; }
    public string PlayerDescription { get; set; }
    ....
    ....
    .... Following this pattern for all of the values in each player element
    ....
    ....
}

我确信这是使用 JSON.NET 做的一件非常简单的事情,这就是为什么我想避免我对如何实现这一点的任何想法.我想出的东西会不优雅,并且可能在序列化过程中在某种程度上容易出错.

I am sure this is a pretty straightforward thing to do using JSON.NET, which is why I wanted to avoid any of the ideas I had on how to accomplish this. What I came up with would have been un-elegant and probably error prone to some degree during the serialization process.

编辑

以下是按照 snow_FFFFFF 的建议使用过去作为 JSON 类时生成的类::>

Here are the classes that get generated when using the past as JSON classes as suggested by snow_FFFFFF:

public class Rootobject
{
    public int timestamp { get; set; }
    public int total_players { get; set; }
    public int max_score { get; set; }
    public Players players { get; set; }
}

public class Players
{
    public object[] Player1Username { get; set; }
    public object[] Player2Username { get; set; }
}

我不清楚如何将players"元素中的 JSON 数据反序列化为 List,其中 Player1Username 是 Player 对象上的一个简单字符串属性.至于混合字符串和整数的集合,我相信我可以毫无问题地将它们放入 Player 对象的各个属性中.

What is not clear to me is how do I deserialize the JSON data in the "players" element as a List with Player1Username being a simple string property on the Player object. As for the collection of intermixed strings and integers, I am confident I can get those into individual properties on the Player object without issue.

推荐答案

转换器在 Visual Basic .NET 中反序列化 JSON 应该可以满足您的需求,从 VB.NET 适当地转换为 c#:

The converter from Deserializing JSON in Visual Basic .NET should do what you need, suitably translated from VB.NET to c#:

public class ObjectToArrayConverter<T> : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return typeof(T) == objectType;
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var objectType = value.GetType();
        var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract;
        if (contract == null)
            throw new JsonSerializationException(string.Format("invalid type {0}.", objectType.FullName));
        writer.WriteStartArray();
        foreach (var property in SerializableProperties(contract))
        {
            var propertyValue = property.ValueProvider.GetValue(value);
            if (property.Converter != null && property.Converter.CanWrite)
                property.Converter.WriteJson(writer, propertyValue, serializer);
            else
                serializer.Serialize(writer, propertyValue);
        }
        writer.WriteEndArray();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        var contract = serializer.ContractResolver.ResolveContract(objectType) as JsonObjectContract;
        if (contract == null)
            throw new JsonSerializationException(string.Format("invalid type {0}.", objectType.FullName));

        if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
            return null;
        if (reader.TokenType != JsonToken.StartArray)
            throw new JsonSerializationException(string.Format("token {0} was not JsonToken.StartArray", reader.TokenType));

        // Not implemented: JsonObjectContract.CreatorParameters, serialization callbacks, 
        existingValue = existingValue ?? contract.DefaultCreator();

        using (var enumerator = SerializableProperties(contract).GetEnumerator())
        {
            while (true)
            {
                switch (reader.ReadToContentAndAssert().TokenType)
                {
                    case JsonToken.EndArray:
                        return existingValue;

                    default:
                        if (!enumerator.MoveNext())
                        {
                            reader.Skip();
                            break;
                        }
                        var property = enumerator.Current;
                        object propertyValue;
                        // TODO:
                        // https://www.newtonsoft.com/json/help/html/Properties_T_Newtonsoft_Json_Serialization_JsonProperty.htm
                        // JsonProperty.ItemConverter, ItemIsReference, ItemReferenceLoopHandling, ItemTypeNameHandling, DefaultValue, DefaultValueHandling, ReferenceLoopHandling, Required, TypeNameHandling, ...
                        if (property.Converter != null && property.Converter.CanRead)
                            propertyValue = property.Converter.ReadJson(reader, property.PropertyType, property.ValueProvider.GetValue(existingValue), serializer);
                        else
                            propertyValue = serializer.Deserialize(reader, property.PropertyType);
                        property.ValueProvider.SetValue(existingValue, propertyValue);
                        break;
                }
            }
        }
    }

    static IEnumerable<JsonProperty> SerializableProperties(JsonObjectContract contract)
    {
        return contract.Properties.Where(p => !p.Ignored && p.Readable && p.Writable);
    }
}

public static partial class JsonExtensions
{
    public static JsonReader ReadToContentAndAssert(this JsonReader reader)
    {
        return reader.ReadAndAssert().MoveToContentAndAssert();
    }

    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

接下来,将转换器添加到您的 Player 类,并使用 JsonPropertyAttribute.Order:

Next, add the converter to your Player class, and indicate the order of each property using JsonPropertyAttribute.Order:

[JsonConverter(typeof(ObjectToArrayConverter<Player>))]
public class Player
{
    [JsonProperty(Order = 1)]
    public int UniqueID { get; set; }
    [JsonProperty(Order = 2)]
    public string PlayerDescription { get; set; }
    // Other fields as required.
}

最后,声明你的根对象如下:

Then finally, declare your root object as follows:

public class ScoreboardResults
{
    public int timestamp { get; set; }
    public int total_players { get; set; }
    public int max_score { get; set; }
    public Dictionary<string, Player> players { get; set; }
}

请注意,我已将 Username 作为键从 Player 类移到字典中.

Note that I have moved Username out of the Player class and into the dictionary, as a key.

注意数据契约属性可以代替Newtonsoft指定顺序的属性:

Note that data contract attributes can be used instead of Newtonsoft attributes to specify order:

[JsonConverter(typeof(ObjectToArrayConverter<Player>))]
[DataContract]
public class Player
{
    [DataMember(Order = 1)]
    public int UniqueID { get; set; }
    [DataMember(Order = 2)]
    public string PlayerDescription { get; set; }
    // Other fields as required.
}

演示小提琴这里此处此处.

这篇关于如何将具有固定架构的值数组反序列化为强类型数据类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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