Deserialize a JSON array as Dictionary with JSON.Net - json

I've one part of my JSON that looks like this:
Like you can see, in the JSON, the temperature "dictionary", is in fact a list of list of 2 element.
The first element is a timestamp, the second the temperature. Not sure why the provider of the service did it like this, but I don't really have the choice, I must do with it.
But in my C# object, I would like to have this as a dictionary, with timestamp as the key, and temperature as the value.
Is this possible?
//note, I've a custom converter that converts from long to DateTime
public Dictionary<DateTime, double> Temperature { get; set; }
and deserializing like this:
JsonConvert.DeserializeObject<List<WeatherPredictionDay>>(content, new EpochConverter());

Yes, this can be done with a custom JsonConverter like this:
class TemperatureArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Dictionary<DateTime, double>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray ja = JArray.Load(reader);
var dict = new Dictionary<DateTime, double>();
foreach (JArray item in ja)
{
var key = item[0].ToObject<DateTime>(serializer);
var val = item[1].ToObject<double>(serializer);
dict.Add(key, val);
}
return dict;
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use it, just mark your Temperature property with a [JsonConverter] attribute like this:
[JsonConverter(typeof(TemperatureArrayConverter))]
public Dictionary<DateTime, double> Temperature { get; set; }
Note: the above converter as written is intended to work with your existing EpochConverter to convert the timestamp values into DateTimes for the dictionary keys.
Here is a working demo: https://dotnetfiddle.net/TdxYjj

Related

How to define SingleOrArrayConverter to Handle JSON property in F# ( Port from C# )

I am porting some ( well tested over time ) code from C# to F# and having some issues getting something to work in F#
The C# Code:
( Object I want to serialise )
public class Locality
{
public string category { get; set; }
public int id { get; set; }
public string location { get; set; }
public string postcode { get; set; }
public string state { get; set; }
public double? latitude { get; set; }
public double? longitude { get; set; }
}
public class Localities
{
[JsonProperty("locality")]
[JsonConverter(typeof(R2H.Models.JSon.SingleOrArrayConverter<Locality>))]
public List<Locality> locality { get; set; }
}
public class AuspostPostCodeLocality
{
public Localities localities { get; set; }
}
( JSON converter )
public class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
( Attempt at F# Code )
type SingleOrArrayConverter<'T>() =
inherit JsonConverter()
override this.CanConvert(objectType : Type) =
objectType = typeof<List<'T>>
override this.ReadJson(reader : JsonReader, objectType : Type, existingValue : System.Object, serializer : JsonSerializer) =
let mutable (token : JToken) = JToken.Load (reader)
if token.Type = JTokenType.Array then
(token.ToObject<List<'T>> ())
else
([|(token.ToObject<'T> ())|])
override this.CanWrite
with get() =
false
override this.WriteJson(writer : JsonWriter, value : System.Object, serializer : JsonSerializer) =
raise (new NotImplementedException() :> System.Exception)
And my attempt at the model ( You can see several attempts commented out ).
type Locality = {
category: string
id: int
location: string
postcode: int
state: string
latitude: decimal
longitude: decimal
}
type Localities = {
//inherit JsonConverter<SingleOrArrayConverter<Locality>>()
//[<JsonProperty("locality");JsonConverter<SingleOrArrayConverter<Locality>>>]
//[<JsonConverter(typeof (SingleOrArrayConverter<Locality>))>]
//[<JsonProperty("locality")>]
locality: List<Locality>
}
type PostCodeLocality = {
localities : Localities
}
Since your converter seems to have originated from this answer by Brian Rogers to How to handle both a single item and an array for the same property using JSON.net, I assume that you are trying to create a generic converter for mutable lists of type System.Collections.Generic.List<'T> (abbreviated to ResizeArray in FSharpx.Collections).
F# also has an immutable list type FSharp.Collections.List<'T> abbreviated to list. Be sure which one you want to use.[1]
With that in mind, assuming you want System.Collections.Generic.List<'T> your converter can be written as follows:
type SingleOrArrayConverter<'T>() =
inherit JsonConverter()
override this.CanConvert(objectType) = objectType = typeof<ResizeArray<'T>>
override this.ReadJson(reader, objectType, existingValue, serializer) = // Unlike in C# it's not necessary to declare the types of the arguments when there is no ambiguity
let token = JToken.Load (reader)
if token.Type = JTokenType.Array then
// Upcast to obj as upcasting is not automatic for returned value
// https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/casting-and-conversions#upcasting
(token.ToObject<ResizeArray<'T>> ()) :> obj
else
ResizeArray [|(token.ToObject<'T> ())|] :> obj // Convert array to List<T> then upcast to object
override this.CanWrite = false // Simplified syntax
override this.WriteJson(writer, value, serializer) = raise (new NotImplementedException())
And your Localities type defined as follows:
type Localities = {
[<JsonProperty("locality")>]
[<JsonConverter(typeof<SingleOrArrayConverter<Locality>>)>] // Fix syntax: typeof<'T> not typeof(T)
locality: ResizeArray<Locality> // Be sure whether you want System.Collections.Generic.List<'T> a.k.a ResizeArray<'T> or FSharp.Collections.List<'T>
}
Demo fiddle #1 here.
If you do want to use f#'s immutable list, define your converter as follows:
type SingleOrArrayFSharpListConverter<'T>() =
inherit JsonConverter()
override this.CanConvert(objectType) = objectType = typeof<list<'T>>
override this.ReadJson(reader, objectType, existingValue, serializer) = // Unlike in C# it's not necessary to declare the types of the arguments when there is no ambiguity
let token = JToken.Load (reader)
if token.Type = JTokenType.Array then
// Upcast to obj as upcasting is not automatic for returned value
// https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/casting-and-conversions#upcasting
(token.ToObject<list<'T>> ()) :> obj
else
[token.ToObject<'T>()] :> obj // Convert array to list<T> then upcast to object
override this.CanWrite = false // Simplified syntax
override this.WriteJson(writer, value, serializer) = raise (new NotImplementedException())
And modify Localities as follows:
type Localities = {
[<JsonProperty("locality")>]
[<JsonConverter(typeof<SingleOrArrayFSharpListConverter<Locality>>)>] // Fix syntax: typeof<'T> not typeof(T)
locality: Locality list // Be sure whether you want System.Collections.Generic.List<'T> a.k.a ResizeArray<'T> or FSharp.Collections.List<'T>
}
Demo fiddle #2 here.
Update
It isn't actually necessary to preload the JSON into a JToken to check to see whether it is an array, you can just check the value of JsonReader.TokenType. The following version of SingleOrArrayConverter<'T> does this and so should be more efficient:
module JsonExtensions =
type JsonReader with
member r.ReadAndAssert() =
if not (r.Read()) then raise (JsonReaderException("Unexpected end of JSON stream."))
r
member r.MoveToContentAndAssert() =
if r.TokenType = JsonToken.None then r.ReadAndAssert() |> ignore
while r.TokenType = JsonToken.Comment do r.ReadAndAssert() |> ignore
r
open JsonExtensions
type SingleOrArrayConverter<'T>() =
inherit JsonConverter()
override this.CanConvert(objectType) = objectType = typeof<ResizeArray<'T>>
override this.ReadJson(reader, objectType, existingValue, serializer) =
let a = if (existingValue :? ResizeArray<'T>) then existingValue :?> ResizeArray<'T> else ResizeArray<'T>() // Reuse the incoming List<T> if preallocated
match reader.MoveToContentAndAssert().TokenType with
| JsonToken.StartArray -> serializer.Populate(reader, a)
| _ -> a.Add(serializer.Deserialize<'T>(reader))
a :> obj
override this.CanWrite = false // Simplified syntax
override this.WriteJson(writer, value, serializer) = raise (new NotImplementedException())
Demo fiddle #3 here.
[1] For details see Creating a generic List <T> in F#.

Formatting Json for WebAPI 2 from Ember-Data

I've been struggling with this problem for a few days now and was hoping someone could help me out. I've got an ember front end which communicates with an API written in C#.
I'm making a an update (PUT) request but the model comes through with all the properties as null. The reason for this is because the API expects
{
"Type":1,
"Name":"Hello World"
}
but ember sends it in the format of
{
"Object":
{
"Type":1,
"Name":"Hello World"
}
}
I've seen solutions where you can add a custom JsonConverter onto the API Model, but this has to have it's own converter for each model. I'd like to know if someone help me with a generic one that I can apply to any model.
Here's an example of one that is specific for the profile model, I can't seem to get it to work as a generic though.
[JsonConverter(typeof(ProfileConverter))]
public class Profile
{
public string LoginId { get; set; }
public bool NewProfileOptIn { get; set; }
}
and the converter
public class ProfileConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Profile).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
Log.Information("Reading Json from ProfileSetting");
JToken token = JToken.Load(reader);
if (token["ProfileSetting"] != null)
{
JObject inner = token["ProfileSetting"].Value<JObject>();
if (CheckJsonProperty(inner, "login_id") && CheckJsonProperty(inner, "new_profile_opt_in"))
{
Profile model = new Profile
{
LoginId = inner["login_id"].ToString(),
NewProfileOptIn = (bool)inner["new_profile_opt_in"]
};
return model;
}
return null;
}
Log.Error("Invalid Model Name Passed - Expected 'ProfileSetting'");
return null;
}
private bool CheckJsonProperty(JObject objectToCheck, string expectedJsonProperty)
{
if (objectToCheck[expectedJsonProperty] != null)
{
return true;
}
Log.Error("Invalid or Missing Model Property Name Passed - Expected '" + expectedJsonProperty + "'");
return false;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
If any one has any reading resources or examples that would help, it would be much appreciated.
Thanks,
VB.Net
This Example has been coded with Json.Net.
So I'm working with Vb.net but this should work in C# the same way.
I've been able to do it without an JsonConverter as follows:
Public Class TestController
Inherits ApiController
<HttpGet>
Public Function Index() As String
Dim o As New JObject
o.Add("Data", 10)
Dim j As New JObject
j.Add("Object", o)
Dim obj = j.ToObject(Of EmberDataObject(Of TestObject))()
Return "200 - OK"
End Function
End Class
Public Class EmberDataObject(Of T)
<JsonProperty("Object")>
Public Property DataObject As T
End Class
Public Class TestObject
Public Property Data As Integer
End Class
In the above example the JsonObject with this mark up
{
"Object": {
"Data": 10
}
}
can be transformed easily. You can then access the nested Type as obj.DataObject. Through the set JsonProperty Attribute you can omit the Object key word which at least in Vb is not a legal Property name.
C#
The C# translation would be like this:
public class EmberDataObject<T>
{
[JsonProperty("Object")
public T DataObject {get; set;}
}
public class TestData
{
public int Data {get; set;}
}
And then referencing this from your Controller as follows:
public class MyApiController : ApiController
{
public IActionResult Post(EmberDataObject<TestData> emberObject)
{
/// Inner Object Available as emberObject.Object
}
}

Deserialise dynamic json types

I want to deserialize json which returns different data for different parameters.
Mostly I get:
{"posts": [{ "id" : 1, "name":"post1" },{ "id" : 1, "name":"post1" }]}
But sometimes the data returned is
{"posts": false}
I want to deserialize this as the following class
public class GetReaderResponse
{
public IEnumerable<ReaderPost> posts {get; set;}
}
public class ReaderPost
{
public int id {get; set;}
public string name{get; set;}
}
I am using C#,json.net but not able to do this correctly.
Newtonsoft.Json.JsonConvert.DeserializeObject<GetReaderResponse>(dataString);
You could build a custom converter, but an easy way to handle this would be to write an error handler that detects errors with the posts property:
var settings = new JsonSerializerSettings();
settings.Error += (sender, args) =>
{
if (string.Equals("posts", args.ErrorContext.Path, StringComparison.OrdinalIgnoreCase))
{
var currentObject = args.CurrentObject as GetReaderResponse;
currentObject.posts = Enumerable.Empty<ReaderPost>();
args.ErrorContext.Handled = true;
}
};
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
This sets posts to Enumerable.Empty<ReaderPost>. This is still a little unsatisfying because if any error occurs, the property will be set. You could build a full custom converter to do this as a more complete solution.
Here's a converter that will take care of this:
public class PostsConverter : JsonConverter
{
public override object ReadJson(
JsonReader reader,
Type objectType,
object existingValue,
JsonSerializer serializer)
{
JToken val = JValue.ReadFrom(reader);
object result = null;
if (val.Type == JTokenType.Array)
{
result = val.ToObject<IEnumerable<ReaderPost>>();
}
else if (val.Type == JTokenType.Boolean)
{
result = Enumerable.Empty<ReaderPost>();
}
return result;
}
public override void WriteJson(
JsonWriter writer,
object value,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert (Type type)
{
return typeof(IEnumerable<ReaderPost>).IsAssignableFrom(type);
}
public override bool CanRead
{
get { return true; }
}
}
Usage:
var settings = new JsonSerializerSettings();
settings.Converters = new [] { new PostsConverter() };
GetReaderResponse resp =
JsonConvert.DeserializeObject<GetReaderResponse>(json, settings);
Example: https://dotnetfiddle.net/i9CXwp
By using JSON.NETs built in LINQ to JSON, you can try someting like this:
JObject jObject = JObject.Parse(json);
GetReaderResponse response = new GetReaderResponse();
if (jObject["posts"] is JArray)
response = jObject.ToObject<GetReaderResponse>();
// Do something with the response object.
where json variable is the json string you need to deserialize.
try this:
public class GetReaderResponse
{
public bool posts { get; set; }
public ReaderPost[] post { get; set; }
}
After reading #Ilija's comment I think I might have found a answer. I did not want not use string literals so I modified my class GetReaderResponse to look like below:
public class GetReaderResponse
{
public dynamic posts {get; set;}
public IEnumerable<ReaderPost> Posts
{
get
{
if (posts is bool )
return new ReaderPost[0];
return posts.ToObject<IEnumerable<ReaderPost>>();
}
}
}
Does this sound fine or does it look messy?

How do I limit NewtonSoft.JSON to serialize an interface?

Very simple example (I use this in my unit tests):
private interface ISampleSubset
{
int id { get; }
}
private class Sample : ISampleSubset
{
public int id { get; set; }
public string name { get; set; }
}
Here's a small wrapper around NewtonSoft's JSON Serialize:
public string Serialize<T>(T t)
{
using (var sw = new StringWriter())
{
using (var jw = new JsonTextWriter(sw))
{
var js = JsonSerializer.Create();
js.Serialize(jw, t);
jw.Flush();
}
return sw.GetStringBuilder().ToString();
}
}
Now I want to serialize ISampleSubset:
And call it like so:
ISampleSubSet t = new Sample()
{
id = 1,
name = "joe"
};
string json = Serialize(t);
I expect to get
{"id":1}
but instead I get
{"id":1,"name":"joe"}
I'm guessing js.Serialize is using reflection to 'see' the other properties on the object 'outside' of the interface. How do I limit it to just those properties on the interface?
The serializer doesn't even know about your interface, so it's giving you everything - it accepts an object, so it doesn't know that you've declared your variable of type ISampleSubset - all it knows is the object itself is an instance of Sample.
Probably not the best solution, but you can use a JsonConverter to restrict the properties that appear in your serialized object.
This code is probably very inefficient - please don't judge - just threw it together, you can clean up the details and implement however you need:
public class MyConverter<T> : JsonConverter {
private readonly string[] _propertyNames;
public MyConverter() {
_propertyNames = typeof(T).GetProperties().Select(p => p.Name).ToArray();
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
var objectType = value.GetType();
var newObject = new Dictionary<string, object>();
foreach (string propName in _propertyNames) {
var prop = objectType.GetProperty(propName);
if (prop != null) {
newObject[propName] = prop.GetValue(value, null);
}
}
string s = JsonConvert.SerializeObject(newObject);
writer.WriteRaw(s);
}
public override bool CanConvert(Type objectType) {
return true; // ?
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
public static string Serialize<T>(T t) {
return JsonConvert.SerializeObject(t, new MyConverter<T>());
}
Basically what it's doing is using reflection on the interface type to retrieve its properties, then creating a dictionary using only the properties found on the interface (you can do that a number of ways) - then using the simple JsonConvert class to serialize the dictionary.
NewtonSoft.JSON is serializing the object instance you created based on the default rule (as of .NET 3.5 IIRC) that all properties of an object are by default serializable. It doesn't matter if you declared your variable as an interface type because it's probably doing the serialization by reflection.
If you want to restrict the properties that get serialized the best way is to use theNonSerialized attribute.

How to get the name of the property for which the custom deserialization (ReadJson) is invoked?

I have a suitation. I want to do a custom Deserialization for some properties. But i want to handle it in a single convertor class. Dont want to write seperate for each class.
So is there any way to find the Property Name for which the ReadJson is invoked?
// My Class
public class SomeClass
{
// Private members
private double m_nValue;
private string m_strValue;
// Properties
[JsonConverter(typeof(AlfhaConverter))]
public double Value
{
get { return m_nValue; }
set { m_nValue = value; }
}
[JsonConverter(typeof(AlfhaConverter))]
public string StrValue
{
get { return m_strValue; }
set { m_strValue = value; }
}
}
// JsonConverter
public class PropertyConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//Here i want to find the property name so that i can perform certain steps based on the property
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}