我在寻找一种干净的(尽可能)的方式来反序列化某些特定格式的JSON数据时遇到了一些麻烦。我想将数据反序列化为强类型数据对象类,这在细节方面非常灵活。这是数据的示例:
{ "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"] } }
我不确定的具体部分是:
说我有一个像这样的顶级课程:
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,第二个值是玩家说明,等等。我希望玩家类是这样的:
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要做的一件非常简单的事情,这就是为什么我想要避免我对实现此想法有任何想法的原因。我想出的东西在序列化过程中可能很不雅致,并且可能在一定程度上容易出错。
编辑
这是snow_FFFFFF建议将过去用作JSON类时生成的类:
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对象的各个属性中。
从Visual Basic .NET中的反序列化JSON进行转换的转换器应该执行您所需的操作,并将其适当地从VB.NET转换为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:
Player
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. }
最后,按如下所示声明您的根对象:
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班级进入字典。
Username
演示在这里和这里摆弄。