I have a very similar issue to this question here, except my application is in C#, and I can't figure out how to convert the solution unfortunately. I am trying to deserialize a JSON result that looks like this:
"error":[],
"result":
{
"MANAEUR":[
[1619042400,"1.11200","1.13488","1.08341","1.10077","1.09896","58878.56534370",137],
[1619046000,"1.09767","1.12276","1.08490","1.11097","1.10456","25343.25910419",77],
],
"last":1619118000
}
I use the following classes:
public class ResponseBase
{
[JsonProperty(PropertyName = "error")]
public List<string> Error;
}
public class OHLCResponse : ResponseBase
{
[JsonProperty("result")]
public OHLCResult Result;
}
public class OHLCResult
{
[JsonProperty("pair_names")]
public Dictionary<string, OHLC[]> GetHistory;
[JsonProperty("last")]
public long Last;
}
.... and then finally the guts of it:
public class OHLC
{
public int Time;
public decimal Open;
public decimal High;
public decimal Low;
public decimal Close;
public decimal Vwap;
public decimal Volume;
public int Count;
}
I have a standard deserializer class which works for all other calls I am using to the same API, but I cannot get this call to work. When I retrieve OHLCResponse object,I don't get an error, and "Result.Last" is always populated, but the expected array of OHLC items in "Result.GetHistory" is always empty/null. I know that the data has been returned successfully since I can see the data in the variable returned from the WebRequest that I am then passing to the deserializer function, so it must be that I have these classes laid out incorrectly I guess.
Can anyone see what I'm doing wrong?
Many thanks in advance, Dave
The object you posted isn't valid JSON. The outside curly braces are missing. So I am going to assume it should look like this:
{
"error": [],
"result": {
"MANAEUR": [
[1619042400, "1.11200", "1.13488", "1.08341", "1.10077", "1.09896", "58878.56534370", 137],
[1619046000, "1.09767", "1.12276", "1.08490", "1.11097", "1.10456", "25343.25910419", 77],
],
"last": 1619118000
}
}
Anonymous Deserialization
The first method you could do, which may be a tad kludgy since you have to deserialize twice, is use anonymous deserialization.
Let's start by defining some models:
public sealed class OHLCModel
{
public long Time { get; set; }
public decimal Open { get; set; }
public decimal High { get; set; }
public decimal Low { get; set; }
public decimal Close { get; set; }
public decimal Vwap { get; set; }
public decimal Volume { get; set; }
public int Count { get; set; }
}
public sealed class ResultModel
{
[JsonIgnore]
public IEnumerable<OHLCModel> Manaeur { get; set; }
[JsonProperty("last")]
public long Last { get; set; }
}
public sealed class RootModel
{
[JsonProperty("error")]
public List<string> Error { get; set; }
[JsonProperty("result")]
public ResultModel Result { get; set; }
}
As you can see we are ignoring the Manaeur object when serialization happens.
To make this method work, we'd do this:
var json = System.IO.File.ReadAllText(#"c:\users\andy\desktop\test.json");
// First, just grab the object that has the mixed arrays.
// This creates a "template" of the format of the target object
var dto = JsonConvert.DeserializeAnonymousType(json, new
{
Result = new
{
Manaeur = new List<List<object>>()
}
});
// Next, deserialize the rest of it
var fullObject = JsonConvert.DeserializeObject<RootModel>(json);
// transfer the DTO using a Select statement
fullObject.Result.Manaeur = dto.Result.Manaeur.Select(x => new OHLCModel
{
Time = Convert.ToInt64(x[0]),
Open = Convert.ToDecimal(x[1]),
High = Convert.ToDecimal(x[2]),
Low = Convert.ToDecimal(x[3]),
Close = Convert.ToDecimal(x[4]),
Vwap = Convert.ToDecimal(x[5]),
Volume = Convert.ToDecimal(x[6]),
Count = Convert.ToInt32(x[7])
});
This isn't the most ideal solution as you are tightly coupling to the model in a few spots. The ideal way to do this would be to make a custom JsonSerializer.
Use a Custom JsonConverter
First thing we do is change your ResultModel to look like this:
public sealed class ResultModel
{
[JsonConverter(typeof(ManaeurJsonConverter)), JsonProperty("MANAEUR")]
public IEnumerable<OHLCModel> Manaeur { get; set; }
[JsonProperty("last")]
public long Last { get; set; }
}
Then implement a JsonConverter:
public sealed class ManaeurJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => false; // this will never get called
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var lst = JArray.Load(reader).ToObject<List<List<object>>>();
return lst.Select(x => new OHLCModel
{
Time = Convert.ToInt64(x[0]),
Open = Convert.ToDecimal(x[1]),
High = Convert.ToDecimal(x[2]),
Low = Convert.ToDecimal(x[3]),
Close = Convert.ToDecimal(x[4]),
Vwap = Convert.ToDecimal(x[5]),
Volume = Convert.ToDecimal(x[6]),
Count = Convert.ToInt32(x[7])
});
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{ // we don't need to write
throw new NotImplementedException();
}
}
You can then simply call it as so:
var json = System.IO.File.ReadAllText(#"c:\users\andy\desktop\test.json");
var fullObject = JsonConvert.DeserializeObject<RootModel>(json);
Your JSON is not valid, I had to modify it to make it valid JSON (https://jsonformatter.org/). I added the root brackets and removed the comma delimter after the second inner array entry.
Valid JSON:
{
"error":[],
"result":
{
"MANAEUR":[
[1619042400,"1.11200","1.13488","1.08341","1.10077","1.09896","58878.56534370",137],
[1619046000,"1.09767","1.12276","1.08490","1.11097","1.10456","25343.25910419",77]
],
"last":1619118000
}
}
After updating the JSON, I used Visual Studio's 'Paste Special' to generate C# objects from the JSON. The following classes were created.
public class RootObject
{
[JsonProperty("error")]
public object[] Error { get; set; }
[JsonProperty("result")]
public Result Result { get; set; }
}
public class Result
{
[JsonProperty("MANAEUR")]
public object[][] Manaeur { get; set; }
[JsonProperty("last")]
public int Last { get; set; }
}
With the above JSON and classes, I used the following to deserialize the JSON.
string json = "{\"error\":[],\"result\":{\"MANAEUR\":[[1619042400,\"1.11200\",\"1.13488\",\"1.08341\",\"1.10077\",\"1.09896\",\"58878.56534370\",137],[1619046000,\"1.09767\",\"1.12276\",\"1.08490\",\"1.11097\",\"1.10456\",\"25343.25910419\",77]],\"last\":1619118000}}";
var obj = JsonConvert.DeserializeObject<RootObject>(json);
EDIT:
To handle the MANAEUR property where the key label can be different.
Create a JsonConverter...
public class ManaeurConverter : JsonConverter
{
private Dictionary<string, string> propertyMappings { get; set; }
public ManaeurConverter()
{
this.propertyMappings = new Dictionary<string, string>
{
{"NOTMANAEUR","MANAEUR"}
};
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
object instance = Activator.CreateInstance(objectType);
var props = objectType.GetTypeInfo().DeclaredProperties.ToList();
JObject jo = JObject.Load(reader);
foreach (JProperty jp in jo.Properties())
{
if (!propertyMappings.TryGetValue(jp.Name, out var name))
name = jp.Name;
PropertyInfo prop = props.FirstOrDefault(pi =>
pi.CanWrite && pi.GetCustomAttribute<JsonPropertyAttribute>().PropertyName == name);
prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer));
}
return instance;
}
public override bool CanConvert(Type objectType)
{
return objectType.GetTypeInfo().IsClass;
}
public override bool CanWrite => false;
}
... Add the JsonConverter attribute to the class...
[JsonConverter(typeof(ManaeurConverter))]
public class Result
{
[JsonProperty("MANAEUR")]
public object[][] Manaeur { get; set; }
[JsonProperty("last")]
public int Last { get; set; }
}
... and parse like so...
string json_Manaeur = "{\"error\":[],\"result\":{\"MANAEUR\":[[1619042400,\"1.11200\",\"1.13488\",\"1.08341\",\"1.10077\",\"1.09896\",\"58878.56534370\",137],[1619046000,\"1.09767\",\"1.12276\",\"1.08490\",\"1.11097\",\"1.10456\",\"25343.25910419\",77]],\"last\":1619118000}}";
string json_Not_Manaeur = "{\"error\":[],\"result\":{\"NOTMANAEUR\":[[1619042400,\"1.11200\",\"1.13488\",\"1.08341\",\"1.10077\",\"1.09896\",\"58878.56534370\",137],[1619046000,\"1.09767\",\"1.12276\",\"1.08490\",\"1.11097\",\"1.10456\",\"25343.25910419\",77]],\"last\":1619118000}}";
var objManaeur = JsonConvert.DeserializeObject<RootObject>(json_Manaeur);
var objNotManaeur = JsonConvert.DeserializeObject<RootObject>(json_Not_Manaeur);
I am trying to consume an external web service and I am using .NET Core and the Flurl framework. I get a response from the service like below:
[
"Successful Request: 96 Results",
[
{
"eventdate":"2019-10-18",
"name":"",
"url":"",
"info":"",
"showtime":null,
"url_tix":"",
"event_owner":"xxx",
"follow_url":"xxx",
"event_image":"xxx",
"venue":"xxx",
"city":"xxx",
"country":"xxx",
"state":""
}
]
]
and I have a C# entity definition like below:
public class ServiceResponce
{
public Event[] Events { get; set; }
}
public class Event
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public object UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public Uri FollowUrl { get; set; }
[JsonProperty("event_image")]
public object EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
When I tried to call the Flurl method to consume the web service like below:
var result = await serviceUrl.GetJsonAsync<ServiceResponce>();
I got the error mentioned below:
Cannot deserialize the current JSON array (e.g. [1,2,3]) into type
'xxx.ServiceResponce' because the type requires a JSON object (e.g.
{"name":"value"}) to deserialize correctly. To fix this error either
change the JSON to a JSON object (e.g. {"name":"value"}) or change the
deserialized type to an array or a type that implements a collection
interface (e.g. ICollection, IList) like List that can be
deserialized from a JSON array. JsonArrayAttribute can also be added
to the type to force it to deserialize from a JSON array. Path '',
line 1, position 1.
Do you have any solution for that? Any help always is welcome.
The problem here is the JSON response is actually an array of mixed types. The first element of the array is a string, and the second element is an array of event objects. You will need a custom JsonConverter to deserialize this JSON.
Here is the code you would need for the converter:
class ServiceResponceConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(ServiceResponce));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray ja = JArray.Load(reader);
ServiceResponce resp = new ServiceResponce();
resp.Events = ja[1].ToObject<Event[]>(serializer);
return resp;
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, add a [JsonConverter] attribute to the ServiceResponce class to tie it to the converter:
[JsonConverter(typeof(ServiceResponceConverter))]
public class ServiceResponce
{
public Event[] Events { get; set; }
}
Now you can deserialize to the ServiceResponce class as normal and it will work properly.
Optional: If you also want to capture the "Successful Request: 96 Results" string from the response, add
public string ResultString { get; set; }
to the ServiceResponce class and add the following line to the the ReadJson method of the converter:
resp.ResultString = (string)ja[0];
Working demo here: https://dotnetfiddle.net/opPUmX
I think the problem is in the Json object, I have generated a class with 'Newtonsoft.Json', if you can try this code:
// <auto-generated />
//
// To parse this JSON data, add NuGet 'Newtonsoft.Json' then do:
//
// using MyNameSpace;
//
// var event = Event.FromJson(jsonString);
namespace MyNameSpace
{
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class EventClass
{
[JsonProperty("eventdate")]
public DateTimeOffset Eventdate { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("showtime")]
public object Showtime { get; set; }
[JsonProperty("url_tix")]
public string UrlTix { get; set; }
[JsonProperty("event_owner")]
public string EventOwner { get; set; }
[JsonProperty("follow_url")]
public string FollowUrl { get; set; }
[JsonProperty("event_image")]
public string EventImage { get; set; }
[JsonProperty("venue")]
public string Venue { get; set; }
[JsonProperty("city")]
public string City { get; set; }
[JsonProperty("country")]
public string Country { get; set; }
[JsonProperty("state")]
public string State { get; set; }
}
public partial struct EventUnion
{
public EventClass[] EventClassArray;
public string String;
public static implicit operator EventUnion(EventClass[] EventClassArray) => new EventUnion { EventClassArray = EventClassArray };
public static implicit operator EventUnion(string String) => new EventUnion { String = String };
}
public class Event
{
public static EventUnion[] FromJson(string json) => JsonConvert.DeserializeObject<EventUnion[]>(json, MyNameSpace.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this EventUnion[] self) => JsonConvert.SerializeObject(self, MyNameSpace.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
EventUnionConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class EventUnionConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(EventUnion) || t == typeof(EventUnion?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new EventUnion { String = stringValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<EventClass[]>(reader);
return new EventUnion { EventClassArray = arrayValue };
}
throw new Exception("Cannot unmarshal type EventUnion");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (EventUnion)untypedValue;
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
if (value.EventClassArray != null)
{
serializer.Serialize(writer, value.EventClassArray);
return;
}
throw new Exception("Cannot marshal type EventUnion");
}
public static readonly EventUnionConverter Singleton = new EventUnionConverter();
}
}
I am reading data from a graph database and getting the response as a dynamic object. I go through the results and try to deserialize them as so:
var e = results.GetEnumerator();
while (e.MoveNext())
{
var serialized = JsonConvert.SerializeObject(e.Current);
// {"FlagCalculateclientside":[false],"Description":["Some detailed info"], "Name": ["MyDetailedEntity"]}
var val = JsonConvert.DeserializeObject<MyObject>(serialized);
}
public class MyObject
{
public bool FlagCalculateclientside { get; set; }
public string Description { get; set; }
public string Name { get; set; }
}
But I get the following error:
Newtonsoft.Json.JsonReaderException: Unexpected character encountered while parsing value: [. Path 'FlagCalculateclientside', line 1, position 28.
at Newtonsoft.Json.JsonTextReader.ReadAsBoolean()
at Newtonsoft.Json.JsonReader.ReadForType(JsonContract contract, Boolean hasConverter)
...
I guess this is because the values are in arrays, but only a single value per key was expected.
Any idea how to fix this?
Your model doesn't match your JSON, all of the properties are arrays, in other words they are surround with [...]. To fix, change the model to this:
public class MyObject
{
public List<bool> FlagCalculateclientside { get; set; }
public List<string> Description { get; set; }
public List<string> Name { get; set; }
}
An alternative would be to use a custom converter, for example:
public class ArrayConverter<T> : JsonConverter<T>
{
public override T ReadJson(JsonReader reader, Type objectType, T existingValue, bool hasExistingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
//This isn't the best code but shows you what you need to do.
return token.ToObject<List<T>>().First();
}
public override void WriteJson(JsonWriter writer, T value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And change your model to this:
public class MyObject
{
[JsonConverter(typeof(ArrayConverter<bool>))]
public bool FlagCalculateclientside { get; set; }
[JsonConverter(typeof(ArrayConverter<string>))]
public string Description { get; set; }
[JsonConverter(typeof(ArrayConverter<string>))]
public string Name { get; set; }
}
I need to convert JSON object to entity,
I have many entities and I don't want to write my code some times, so I built an entity-base class and I want to do deserialize to entity (without know which derived-entity call to the base-entity).
Can I do it?
This is my base class (It is abstract class)
public abstract class AbstractEntity
{
EntityState EntityState { get; set; }
DateTime CreatedDate { get; set; }
DateTime? ModifiedDate { get; set; }
string CreatedBy { get; set; }
string ModifiedBy { get; set; }
public EntityState getEntityState()
{
return EntityState;
}
public void SetEntityState(EntityState entityState)
{
EntityState = entityState;
}
}
The first ent:
public class TABLE1: AbstractEntity
{
public TABLE1();
public string ID{ get; set; }
public string ADDRESS{ get; set; }
public virtual ICollection<TABLE2> TABLE2 { get; set; }
}
The second ent:
public class TABLE2: AbstractEntity
{
public TABLE2();
public string ID{ get; set; }
public string ADDRESS{ get; set; }
public virtual TABLE1 TABLE1{ get; set; }
}
The duplicate linked is probably a better general-purpose solution:
public class AbstractEntityConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(AbstractEntity);
}
public override object ReadJson(JsonReader reader,
Type objectType, object existingValue, JsonSerializer serializer)
{
JObject item = JObject.Load(reader);
var type = item["TYPE"].Value<string>();
switch (type)
{
case "TABLE1":
return item.ToObject<TABLE1>();
case "TABLE2":
return item.ToObject<TABLE2>();
default:
return null;
}
}
public override void WriteJson(JsonWriter writer,
object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Usage:
string json = "{\"TYPE\":\"TABLE1\",\"CreatedDate\":\"2018-06-10T08:00:00.000Z\",\"CreatedBy\":\"John\",\"ID\":\"1\",\"ADDRESS\":\"1 Road Street\",\"TABLE2\":[{\"CreatedDate\":\"2018-06-10T08:00:00.000Z\",\"CreatedBy\":\"John\",\"ID\":\"2\",\"ADDRESS\":\"2 Road Street\"}]}";
var settings = new JsonSerializerSettings()
{
Converters = new List<JsonConverter>()
{
new AbstractEntityConverter()
}
};
var obj = JsonConvert.DeserializeObject<AbstractEntity>(json, settings);
Of course, you can define your JsonSettings at a central location so that you don't have to keep writing the declaration every time you use it.
P.S. I've opted to assume a "type" property since you didn't show your JSON, or your method of determining which class to deserialize to. Replace with your own logic as needed.
Try it online
You can use JObject from Newtonsoft.Json library. This is the site https://www.newtonsoft.com/json to refer on how to use it. And, a lot of code samples as well can be found here https://www.newtonsoft.com/json/help/html/Samples.htm.
For example:
using Newtonsoft.Json.Serialization;
public void Foo(string jsonData){
var objData = (JObject)JsonConvert.DeserializeObject(jsonData); // Deserialize json data
dynamic jObject = new JObject();
jObject.ID = objData.Value<string>("ID");
jObject.Address = objData.Value<string>("Address");
jObject.TABLE2 = objData.Value<JArray>("TABLE2");
}
In the above code sample, jObject which has a dynamic type that can be converted to which type you want.
Hope this helps.
I have been working with the project, where i have to make external RESTful service call to get some data.
The problem i'm facing here is, the response i'm getting from the service is different on different scenario. For example.
On one scenario, i'm getting below response
{
"id":3000056,
"posted_date":"2016-04-15T07:16:47+00:00",
"current_status":"initialized",
"customer":{
"name" : "George",
"lastName" : "Mike"
},
"application_address":{
"addressLine1" : "Lin1",
"addressLine2" : "Lin2",
}
}
In the other scenario, im getting below response
{
"id":3000057,
"posted_date":"2016-04-15T07:16:47+00:00",
"current_status":"initialized",
"customer":[],
"application_address":[]
}
The problem here is, i have below model, and i'm deserializing it by newtonsoft deserailization.
public class Response
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("posted_date")]
public DateTime PostedDate { get; set; }
[JsonProperty("current_status")]
public string CurrentStatus { get; set; }
[JsonProperty("customer")]
public Customer Customer { get; set; }
[JsonProperty("application_address")]
public ApplicationAddress ApplicationAddress { get; set; }
}
public Class Customer
{
public string name { get; set; }
public string lastName { get; set; }
}
public classs ApplicationAddress
{
public string addreesLine1{ get; set; }
public string addreesLine1{ get; set; }
}
For the first response, it will desrialize. But for the second response, the response is not getting deserialized as the response contains [] for Customer and ApplicationAddrees object. While deserializing, it is treating as a array, but actually it is not.
Note : Below code i'm using for Deserializing.
Response response = JsonConvert.DeserializeObject(result);
Is there any configuration we can do before serializing? Is newtonsoft facilitate that feature?
Thanks .
If you are sure that there will be no arrays in this properties, than you can consider using JsonConverter like this:
public class FakeArrayToNullConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return false;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return null;
}
return token.ToObject<T>();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then put additional attribure to your model:
[JsonProperty("customer")]
[JsonConverter(typeof(FakeArrayToNullConverter<Customer>))]
public Customer Customers { get; set; }
[JsonProperty("application_address")]
[JsonConverter(typeof(FakeArrayToNullConverter<ApplicationAddress>))]
public ApplicationAddress ApplicationAddressList { get; set; }
And when in your JSON string for this properties it will be an array [], you will simply deserialize it with null object.
You can't instruct deserialize to handle "[]" as something different as it represents an array (are you sure you never will get customers and addresses in those arrays??)
So you could deserialize to an anonymous type and then map it to your structure.
This is just a guess but can you check if this works:
public class ApplicationAddress
{
private readonly string[] _array = new string[2];
public string this[int index]
{
get { return _array[index]; }
set { _array[index] = value; }
}
public string addreesLine1
{
get { return this[0]; }
set { this[0] = value; }
}
public string addreesLine2
{
get { return this[1]; }
set { this[1] = value; }
}
}