How to deserialize an array of values with a fixed schema to a strongly typed data class?
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;
}
}
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; }
}
Note that I have moved Username
out of the Player
class and into the dictionary, as a key.
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.
}
Demo fiddles here, here and here.
c# Deserialize unlabelled JSON array
You can use the custom JsonConverter
ObjectToArrayConverter<Full_Result>
from this answer to C# JSON.NET - Deserialize response that uses an unusual data structure to deserialize your JSON into your existing typed data model. Modify Full_Result
as follows:
[JsonConverter(typeof(ObjectToArrayConverter<Full_Result>))]
public class Full_Result
{
[JsonProperty(Order = 1)]
public IList<string> values { get; set; }
[JsonProperty(Order = 2)]
public float score { get; set; }
}
And you will now be able to deserialize as follows:
Parsed_JSON result = JsonConvert.DeserializeObject<Parsed_JSON>(JSON);
Notes:
ObjectToArrayConverter<T>
works by mapping the serializable members ofT
to an array, where the array sequence is defined by the value of theJsonPropertyAttribute.Order
attribute applied to each member. Data contract attributes withDataMemberAttribute.Order
set could be used instead, if you prefer.In your JSON the "score" values are not actually numbers:
score_1
score_2I am assuming that this is a typo in the question and that these values are in fact well-formed numbers as defined by the JSON standard.
Sample fiddle here.
How to deserialize a JSON array into an object using Json.Net?
Json.Net does not have a facility to automatically map an array into a class. To do so you need a custom JsonConverter
. Here is a generic converter that should work for you. It uses a custom [JsonArrayIndex]
attribute to identify which properties in the class correspond to which indexes in the array. This will allow you to easily update your model if the JSON changes. Also, you can safely omit properties from your class that you don't need, such as Filler
.
Here is the code:
public class JsonArrayIndexAttribute : Attribute
{
public int Index { get; private set; }
public JsonArrayIndexAttribute(int index)
{
Index = index;
}
}
public class ArrayToObjectConverter<T> : JsonConverter where T : class, new()
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
var propsByIndex = typeof(T).GetProperties()
.Where(p => p.CanRead && p.CanWrite && p.GetCustomAttribute<JsonArrayIndexAttribute>() != null)
.ToDictionary(p => p.GetCustomAttribute<JsonArrayIndexAttribute>().Index);
JObject obj = new JObject(array
.Select((jt, i) =>
{
PropertyInfo prop;
return propsByIndex.TryGetValue(i, out prop) ? new JProperty(prop.Name, jt) : null;
})
.Where(jp => jp != null)
);
T target = new T();
serializer.Populate(obj.CreateReader(), target);
return target;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, you need to mark up your ChildModel
class as shown below:
[JsonConverter(typeof(ArrayToObjectConverter<ChildModel>))]
class ChildModel
{
[JsonArrayIndex(0)]
public int ID { get; set; }
[JsonArrayIndex(1)]
public string StatusId { get; set; }
[JsonArrayIndex(2)]
public DateTime ContactDate { get; set; }
[JsonArrayIndex(3)]
public string State { get; set; }
[JsonArrayIndex(4)]
public string Status { get; set; }
[JsonArrayIndex(5)]
public string CustomerName { get; set; }
[JsonArrayIndex(6)]
public DateTime WorkStartDate { get; set; }
[JsonArrayIndex(7)]
public DateTime WorkEndDate { get; set; }
[JsonArrayIndex(8)]
public string Territory { get; set; }
[JsonArrayIndex(9)]
public string CustType { get; set; }
[JsonArrayIndex(10)]
public int JobOrder { get; set; }
[JsonArrayIndex(12)]
public string Link { get; set; }
}
Then just deserialize as usual and it should work as you wanted. Here is a demo: https://dotnetfiddle.net/n3oE3L
Note: I did not implement WriteJson
, so if you serialize your model back to JSON, it will not serialize back to the array format; instead it will use the default object serialization.
serialzie c# object to json array with different types
I'm not sure that there is a build in way, but you can write your own JsonConverter
using some reflection magic:
public class ChartValueToArrayJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(ChartValue);
}
public override object ReadJson(JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override void WriteJson(JsonWriter writer,
object value,
JsonSerializer serializer)
{
if (value == null) return;
writer.WriteStartArray();
var properties = value.GetType().GetProperties();
foreach (var property in properties)
writer.WriteValue(value.GetType().GetProperty(property.Name).GetValue(value));
writer.WriteEndArray();
}
}
Usage:
JsonConvert.SerializeObject(new ChartValue(), new ChartValueToArrayJsonConverter()) // results in ["0001-01-01T00:00:00",0.0]
or mark your ChartValue
class with [JsonConverterAttribute(typeof(ChartValueToArrayJsonConverter))]
attribute if you want this behavior globally.
Unable to de-serialize json string
Your model should be:
public class Data
{
public List<List<object>> MyValues { get; set; }
}
public class MyData
{
public string status { get; set; }
public Data data { get; set; }
}
And then serialize and access to the datas:
using (StreamReader file = System.IO.File.OpenText(@"jd.txt"))
{
Newtonsoft.Json.JsonSerializer serializer = new Newtonsoft.Json.JsonSerializer();
MyData MyData = (MyData)serializer.Deserialize(file, typeof(MyData));
return MyData.data.MyValues[0][0].ToString();
}
How to deserialize array into wrapper object?
If you simply want to capture a collection inside a surrogate wrapper object, the easiest way to do so is to make the wrapper appear to be a read-only collection to Json.NET. To do that, you must:
- Implement
IEnumerable<T>
for someT
(hereint
). - Add a constructor that takes an
IEnumerable<T>
for the sameT
. (From experimentation, a constructor that takesT []
is not sufficient.)
Thus if you define your ArrayWrapper
as follows:
public struct ArrayWrapper : IEnumerable<int>
{
private readonly int[] array;
public int Item0 { get { return array[ 0 ]; } }
public int Item1 { get { return array[ 1 ]; } }
public ArrayWrapper(int[] array) {
this.array = array;
}
public ArrayWrapper(IEnumerable<int> enumerable)
{
this.array = enumerable.ToArray();
}
public static implicit operator ArrayWrapper(int[] array) {
return new ArrayWrapper( array );
}
public IEnumerator<int> GetEnumerator()
{
return (array ?? Enumerable.Empty<int>()).GetEnumerator();
}
#region IEnumerable Members
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
You will be able to serialize and deserialize Obj
into the following JSON:
{"Array":[[1,101]]}
Demo fiddle #1 here.
However, in comments you mention your array actually has a fixed schema as documented in Public Rest API for Binance: Kline/Candlestick data. If so, you could adopt the approach from this answer to C#: Parsing a non-JSON array-only api response to a class object with x properties which specifically addresses Binance Kline/Candlestick data:
- Define an explicit data model for your data.
- Label each property with
[JsonProperty(Order = N)]
to indicate relative array positions. ([DataContract]
and[DataMember(Order = N)]
could be used instead.) - Use the converter
ObjectToArrayConverter<ArrayWrapper>()
from this answer to C# JSON.NET - Deserialize response that uses an unusual data structure.
I.e. for the specific model shown in your question, modify its definition as follows:
[JsonConverter(typeof(ObjectToArrayConverter<ArrayWrapper>))]
public struct ArrayWrapper
{
[JsonProperty(Order = 1)]
public int Item0 { get; set; }
[JsonProperty(Order = 2)]
public int Item1 { get; set; }
}
And you will be able to (de)serialize the same JSON. Note that the converter is entirely generic and can be reused any time the pattern of (de)serializing an array with a fixed schema into an object arises.
(You might also want to change the struct
to a class
since mutable structs are discouraged.)
Demo fiddles #2 here and #3 here showing the use of a JsonConverter
attribute applied to one of the serializable properties.
Related Topics
Set Focus on Textbox in Wpf from View Model
Httpclient.Getasync(...) Never Returns When Using Await/Async
How to Get C# Enum Description from Value
How to Bind to a Passwordbox in Mvvm
How to Exclude Property from Json Serialization
How to Export Datatable to Excel
Factory Method With Di and Ioc
Calculate the Execution Time of a Method
Suspending Event Not Raising Using Winrt
How to Handle Accessviolationexception
How to Replace Multiple Spaces With a Single Space in C#
Deserializing Json to .Net Object Using Newtonsoft (Or Linq to Json Maybe)
Deciding Between Httpclient and Webclient