Deserialise dynamic json types - json

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?

Related

Asp .Net Core Json deserialize to model

Returns Json format data to me from an api.
However, the type of the "FuelType" property inside the object may be different. For example, 1 time comes as follows:
{
...
fuelType: "gasoline"
...
}}
But then it can happen:
{
...
fuelType: ["gasoline", "any"]
...
}}
If I set the "FuelType" property type on my model to a string, in the second case, Json will give me an error when it arrives, because it can't convert from array to string. No, if I set the type to an array, then, conversely, if a string arrives, it will issue an error because it cannot convert from a string to an array.
In this case, what should I put the "FuelType" property type in my model so that it does not make an error when deserializing?
It all depends on you,what type of value you want to receive?string or List?
I tried with the codes:
public class SomeModel
{
public SomeModel()
{
fuelType = new List<string>();
}
public int Id { get; set; }
public string Name { get; set; }
public List<string> fuelType { get; set; }
//you could move the codes to someservice
public List<string> someresult(string a)
{
var targetlist = new List<string>();
targetlist.Add(a);
return targetlist;
}
public List<string> someresult(List<string> a)
{
var targetlist = new List<string>();
targetlist.AddRange(a);
return targetlist;
}
}
[HttpPost]
public JsonResult SomeAction()
{
var somemodel = new SomeModel() { Id = 1, Name = "name" };
var somevalue = /*"a"*/new List<string>() { "a", "b", "c" };
var targetvalue = somemodel.someresult(somevalue);
somemodel.fuelType.AddRange(targetvalue);
return new JsonResult(somemodel);
}
Result:
try this
var fuelType = JsonConvert.DeserializeObject<MyClass>(json);
public class MyClass
{
[JsonProperty("fuelType")]
private JToken _fuelType;
[JsonIgnore]
public string[] fuelType
{
get {
if (_fuelType==null) return null;
return _fuelType is JArray ? _fuelType.ToObject<string[]>() : new string[] { (string)_fuelType }; }
set { _fuelType = JsonConvert.SerializeObject(value); }
}
}

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

Custom Json Converter With Constructor Arguments

I am trying to create a custom Json converter that has no default constructor and instead takes a factory that is dependency injected by Autofac. When ever I hit the object that uses this converter I get an exception that there is no no-arg constructor to use for the deserialization.
I have an objects and primitives. One of the objects is an abstract base object that I have the converter on. Since this converter is abstract I want to dependency inject a factory into the converter's ReadJson method to make the choice as to what conversion to make.
Currently the code is something like the following:
using System;
using System.Collections.Generic;
using Autofac;
using Newtonsoft.Json.Converters;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;
using Newtonsoft = Newtonsoft.Json;
public class JsonModule : Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<SubThingFactory>()
.As<IFactory>()
.SingleInstance();
builder.Register(c => this.CreateJsonSerializerSettings(c)).SingleInstance();
builder.RegisterType<CamelCasePropertyNamesContractResolver>()
.As<IContractResolver>()
.SingleInstance();
builder.RegisterType<IsoDateTimeConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.RegisterType<SubThingConverter>()
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
builder.Register(c => new StringEnumConverter
{
CamelCaseText = true
})
.As<Newtonsoft.JsonConverter>()
.SingleInstance();
}
private Newtonsoft.JsonSerializerSettings CreateJsonSerializerSettings(IComponentContext context)
{
var settings = new Newtonsoft.JsonSerializerSettings
{
DefaultValueHandling = Newtonsoft.DefaultValueHandling.Ignore,
NullValueHandling = Newtonsoft.NullValueHandling.Ignore,
DateTimeZoneHandling = Newtonsoft.DateTimeZoneHandling.Utc
};
settings.ContractResolver = context.Resolve<IContractResolver>();
foreach (var converter in context.Resolve<IEnumerable<Newtonsoft.JsonConverter>>())
{
settings.Converters.Add(converter);
}
return settings;
}
}
public class ThingBeingDeserialized
{
private string Name;
private SubThing subby;
}
[Newtonsoft.JsonConverterAttribute(typeof(SubThingConverter))]
public abstract class SubThing
{
public string Name { get; set; }
public virtual string GetName()
{
//Uses reflection to get the name from a custom attribute
return this.Name;
}
}
[CustomName("A")]
public class SubThingA : SubThing
{
public int Field1 { get; set; }
}
[CustomName("B")]
public class SubThingB : SubThing
{
public string Field2 { get; set; }
}
public class SubThingConverter : Newtonsoft.JsonConverter
{
//This is Autofac injected in
private readonly IFactory factory;
public SubThingConverter(IFactory factory)
{
this.factory = factory;
}
public override object ReadJson(Newtonsoft.JsonReader reader, Type objectType, object existingValue, Newtonsoft.JsonSerializer serializer)
{
if (reader.TokenType == Newtonsoft.JsonToken.Null)
{
return null;
}
var jsonObject = JObject.Load(reader);
var type = jsonObject["type"].ToString();
return this.factory.GetSubThing(type, jsonObject);
}
public override void WriteJson(Newtonsoft.JsonWriter writer, object value, Newtonsoft.JsonSerializer serializer)
{
var type = value.GetType();
var properties = type.GetProperties();
var jObject = new JObject
{
{ "type", type.Name }
};
foreach (var prop in properties)
{
if (prop.CanRead)
{
var propVal = prop.GetValue(value, null);
if (propVal != null)
{
jObject.Add(prop.Name, JToken.FromObject(propVal, serializer));
}
}
}
jObject.WriteTo(writer);
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(SubThing);
}
}
public interface IFactory
{
SubThing GetSubThing(string type, JObject restOfObj);
}
public class SubThingFactory : IFactory
{
public SubThing GetSubThing(string type, JObject restOfObj)
{
switch (type)
{
case "A":
return new SubThingA
{
Field1 = (int)(restOfObj["Field1"])
};
case "B":
return new SubThingB
{
Field2 = (string)(restOfObj["Field2"])
};
}
return null;
}
}
public class CustomNameAttribute : Attribute
{
public CustomNameAttribute(string name)
{
this.Name = name;
}
public string Name { get; set; }
}
The way I am doing the Autofac injection for the JsonSerializerSettings is by registering the settings such that the settings.Converters will pick up the enumeration of all the JsonConverters that are registered with the Autofac container and the SubThingConverter is registered such that when it is resolved it will have the IFactory resolved for it and the JsonSerializer also comes from the autofac container with these settings.
Even when I skip the dependency injection and use a new JsonSerializer with the JsonSerializerSettings with the custom converter added as
settings.Converters.Add(new SubThingConverter(new SubThingFactory()))
I still get the complaint that the SubThingConverter does not have a no arg constructor.
It seems to me that overridding the settings to explicitly use this converter should be enough. I also tried adding in the object[] params in the JsonConverter attribute on the SubThing, I couldn't get it to work and it seems to need to be a compile time array, which doesn't work with the dependency injection I need to do. Any pointers would be greatly appreciated. Thank you!

Json.Net setting property name as numbers

Is there a short, more clean way to achieve this ?
public class Sidebar
{
[JsonProperty("0")]
public string Hurry = "hurry";
[JsonProperty("1")]
public string Dont_Spam_This_Button = "don't spam this button";
[JsonProperty("2")]
public string Navigation = "navigation";
[JsonProperty("3")]
public string Overview = "overview";
i want to objects to be numbered so is there a way to do that programatically instead of using attributes and manually counting ?
Yes, you can get the result you want using a custom JsonConverter like this one:
class NumberedPropertiesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JObject jo = new JObject();
int count = 0;
foreach (MemberInfo member in value.GetType().GetMembers())
{
object memberVal = null;
if (member.MemberType == MemberTypes.Field)
{
memberVal = ((FieldInfo)member).GetValue(value);
}
else if (member.MemberType == MemberTypes.Property)
{
memberVal = ((PropertyInfo)member).GetValue(value);
}
else
{
continue;
}
JToken token = memberVal != null ? JToken.FromObject(memberVal, serializer) : null;
jo.Add(count.ToString(), token);
count++;
}
jo.WriteTo(writer);
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
To use the converter, just mark your class with a [JsonConverter] attribute specifying the type of the custom converter:
[JsonConverter(typeof(NumberedPropertiesConverter))]
public class Sidebar
{
...
}
One important note: the properties/fields will be numbered according to the order returned by the Type.GetMembers() method. Generally, this will match the order declared in the class; however, if you have a mix of public properties and public fields, then all of the properties will be returned before all of the fields.
Here is a demo:
public class Program
{
public static void Main()
{
Sidebar sb = new Sidebar();
string json = JsonConvert.SerializeObject(sb, Formatting.Indented);
Console.WriteLine(json);
}
}
[JsonConverter(typeof(NumberedPropertiesConverter))]
public class Sidebar
{
public string Foo { get { return "foo property"; } }
public string Hurry = "hurry";
public string Dont_Spam_This_Button = "don't spam this button";
public string Navigation = "navigation";
public string Overview = "overview";
public string Bar { get { return "bar property"; } }
}
Output:
{
"0": "foo property",
"1": "bar property",
"2": "hurry",
"3": "don't spam this button",
"4": "navigation",
"5": "overview"
}
Fiddle: https://dotnetfiddle.net/aZ51qv
What you are doing will give you something like {"0":"hurry",...,"3":"overview"} when what you want is more like ["hurry",...,"overview"]
What you can do, is create a custom converter/serializer, or if you don't need automatic serialization/deserialization in .net, just serialize an object[] array in a class method...
public string AsJson()
{
return JsonConvert.SerializeObject(new object[] {
Hurry, Dont_Spam_This_Button, Navigation, Overview
});
}

JSON.NET deserializing derived classes does not work as expected

I have been searching the forums and the JSON.NET website on this issue and from what I can see I'm correctly following the guidelines but it is not working correctly.
I'm trying to deserialize object from derived classes.
Serializing works fine, but when deserializing it tries to deserialize in to the wrong type.
I'm trying to do this with Windows Phone 8 and JSON.NET 4.5.11
I have the following classes which I am serializing:
public class MyClass : ModelBase
{
public string Title { get; set; }
[JsonProperty(TypeNameHandling = TypeNameHandling.All)]
public MyAction Action {get; set; }
}
public abstract class MyAction : ModelBase
{
[JsonIgnore()]
public abstract ActionType ActionType { get; }
public abstract void Execute();
}
public class SettingsAction : MyAction
{
public override ActionType ActionType
{
get { return ActionType.Settings; }
}
public SettingsType SettingsType {get; set; }
public override void Execute()
{
}
}
public class NoneAction : MyAction
{
public override ActionType ActionType
{
get { return ActionType.None; }
}
public override void Execute()
{
return;
}
}
I serialize it like this:
MyClass obj = new MyClass
{
Action = new SettingsAction()
};
string json = JsonConvert.SerializeObject(
obj,
Formatting.Indented,
new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All });
using (StreamWriter writer = new StreamWriter(stream))
{
writer.Write(json);
}
And it gives me the following JSON:
{
"$type": "Model.MyClass, Model",
"Title": null,
"Action": {
"$type": "Model.SettingsAction, Model",
"SettingsType": 0
}
}
As far as I can see, this is correct, I told it to include the type information and it's correctly included.
The I deserialize it like this:
using (StreamReader r = new StreamReader(stream))
{
string json = r.ReadToEnd();
MyClass obj = JsonConvert.DeserializeObject<MyClass>(json);
}
And I get the following error:
JsonSerializationException: Error setting value to 'SettingsType' on 'Model.NoneAction'
So, although the type is contained in the JSON, on serializing it's ignoring it and of course deserializing it into a different type fails.
Does anyone have an idea why it's not taking the information into account and deserialize to the correct type?
I have found the culprit:
In one of my properties I was doing this:
public MyAction Action
{
get
{
if (_Action == null) {
Action = new NoneAction();
}
return _Action;
}
set
{
if (value != _Action)
{
_Action = value;
NotifyPropertyChanged("Action");
}
}
}
The problem is in the getter, where I create a NoneAction if the obejct is null.
Apparently Json.NET calls into the getter at some point between creating the MyClass object and setting the values of the MyAction object. When it sees that the Action-property is not null, it tries to assign the values instead of overwrite the whole object.