How to deserialize fixed-value value arrays into strongly typed data classes?

I am having trouble figuring out a clean (as much as possible) way to deserialize JSON data in a specific format. I want to deserialize data into strongly typed classes of data objects, quite flexible regarding the features of this. Here is an example of how the data looks:

{ "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 details that I'm not sure about are the following:

  • Will player gathering be seen as a dictionary? The username can serve as a key, but the value throws me away as it will be a mixed collection of string values ​​and integers.
  • The player consists solely of unnamed values. I almost always worked with JSON data that called properties and values ​​(e.g. timestamp, total_players, etc. At the very top).

Let's say I have a top level class:

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

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

 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 .... .... } 

I'm sure this is a pretty simple thing using JSON.NET, so I wanted to avoid any ideas that I had on how to do this. What I came up with would not be elegant and probably a mistake, prone to some degree during the serialization process.

EDIT

Here are the classes that are 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; } } 

I don’t understand how I will deserialize the JSON data in the players element as a List with the name Player1Username, which is a simple string property of the Player object. As for the set of mixed strings and integers, I am sure that I can get them in the individual properties of the Player object without any problems.

+2
source share
3 answers

The converter from JSON deserialization to Visual Basic.NET should do what you need, respectively 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; } } 

Then add the converter to your Player class and specify 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; } } 

Please note that I moved the Username from the Player class to the dictionary as a key.

Demo violin here and here .

+3
source

A good way to get started would be to let visual studio generate your JSON-based class. Open an empty class file and go to EDIT -> PASTE SPECIAL -> PASTE JSON as CLASSES.

This will create a file with the necessary classes for serializing / deserializing JSON.

+3
source

try it

Create a class as shown below

Note. You can use the "Insert Special" option in visual studio to create all JSON related classes

Edit β†’ Paste Special β†’ Paste Json As Classes

it will create all classes related to JSON

Note: see this . I already answer in a similar way.

0
source

Source: https://habr.com/ru/post/1260223/


All Articles