如何将具有固定模式的值数组反序列化为强类型数据类? [英] How to deserialize an array of values with a fixed schema to a strongly typed data class?
问题描述
我在寻找一种干净(尽可能)的方式来反序列化某些特定格式的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:
- 会不会玩家的能力是否被视为字典?用户名可以用作密钥,但是值却让我失望,因为它是字符串和整数值的混合集合。
- 播放器完全由未命名的值组成。我几乎总是使用命名属性和值(例如,时间戳记,total_players等)的JSON数据进行工作。
说我有一个像这样的顶级课程:
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,第二个值是玩家说明,等等。我希望玩家类是这样的: / p>
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.
编辑
以下是根据的建议将过去用作JSON类时生成的类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; }
}
我不清楚我如何在其中反序列化JSON数据玩家元素作为列表,其中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; }
}
请注意,我已经移动了用户名
从 Player
类中并作为键进入字典。
Note that I have moved Username
out of the Player
class and into the dictionary, as a key.
这篇关于如何将具有固定模式的值数组反序列化为强类型数据类?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!