Can a Json converter help with poor Json convention - json.net

Here is an example of they type JSON I get:
"Books": {
"6": {
"Name": "Some book"
},
"7": {
"Name": "Some other book"
}
}
in a perfect world, the api (that I cannot change) would return this instead:
"Books": [
{
"ID": "6",
"Name": "Some book"
},
{
"ID": "7",
"Name": "Some other book"
}]
Is it possible to write a JsonConverter that will push the top example into this:
[DataContract]
public class Book
{
[DataMember(Name = "ID")]
public int ID { get; set; }
[DataMember(Name = "Name")]
public string Name { get; set; }
}
Thanks
}

Unfortunately, the fact that it's just not well-formatted JSON is going to throw any library that handles JSON. The curly brackets will always be interpreted in the same way, so unless someone that understands JSON better than me has a better idea, then I think your best bet is to do some manual processing.
If the mis-formatting is consistent, then you might be able to use regular expressions to replace the mis-placed curly brackets with square brackets, and then parse it is well-formatted JSON.
Good luck!

If your input document's structure is defined fairly rigidly, you could try to just correct the document so it will parse, and then translate the document into your format.
// reference Newtonsoft.Json.dll
// using Newtonsoft.Json
// using Newtonsoft.Json.Linq
string badJson = "\"Books\": { \"6\": { \"Name\": \"Some book\" }, \"7\": { \"Name\": \"Some other book\" } }";
var source = JObject.Parse(string.Format("{{{0}}}", badJson));
var sb = new StringBuilder();
using (var writer = new JsonTextWriter(new StringWriter(sb)))
{
writer.Formatting = Newtonsoft.Json.Formatting.None;
writer.WriteStartObject();
writer.WritePropertyName("Books");
writer.WriteStartArray();
foreach (JToken child in source["Books"])
{
JProperty prop = child as JProperty;
writer.WriteStartObject();
writer.WritePropertyName("ID");
writer.WriteValue(prop.Name);
writer.WritePropertyName("Name");
writer.WriteValue((string)prop.Value["Name"]);
writer.WriteEndObject();
}
writer.WriteEndArray();
writer.WriteEndObject();
}
Console.WriteLine(sb.ToString());
This code produces the following JSON:
{"Books":[{"ID":"6","Name":"Some book"},{"ID":"7","Name":"Some other book"}]}

Related

Build JObject from iterative path

I need to dynamically build a object that I can then serialize to a JSON string. Essentially I'm working off of two Dictionaries that I need to use to create the new object.
var myValues= new Dictionary<string, object>
{
{ "Value1", "Foo" },
{ "Value2", "Bar" }
};
var mappedValues = new Dictionary<string, string>
{
{ "Value1", "Some:Path" },
{ "Value2", "Some:OtherPath }
};
As I loop through I need to be able to build out the Json Object so that I end up with something like:
{
"Some": {
"Path": "Foo",
"OtherPath": "Bar"
}
}
From what I've seen there is no way built into Newtonsoft.Json to do this specifically, but I'm hoping that someone may have an idea of how I might be able to most efficiently accomplish the goal.
It doesn't seem that there is any sort of way to create an element built into Newtonsoft.Json at this time... however I eventually came up with the following solution which works
private JObject DoFoo()
{
var jObject = new JObject();
foreach(var mapping in mappedValues)
{
var queue = new Queue<string>(mapping.Value.Split(':');
var value = myValues[mapping.Key];
SetValueWithPath(jObject, queue, value);
}
return jObject;
}
private void SetValueWithPath(JObject parent, Queue<string> path, object content)
{
var currentNode = path?.FirstOrDefault();
if (string.IsNullOrEmpty(currentNode)) return;
if (path.Count == 1)
{
parent[currentNode] = JToken.FromObject(content);
return;
}
else if (!parent.ContainsKey(currentNode))
{
parent[currentNode] = new JObject();
}
path.Dequeue();
SetValueWithPath(parent[currentNode] as JObject, path, content);
}

Newtonsoft Putting new property in JObject at the right place

Let's say I have an object like this:
{
"Node": "Fruit.Color",
"Response": "Green"
}
How can I create an JObject which looks like this:
{
"Fruit": {
"Color": "Green"
}
}
I am messing around with Jobjects and such but I can't get it to work.
This seems to do the trick
JToken jToken = response;
foreach (var str in node.Split('.').Reverse())
{
var obj = new JObject
{
{ str, jToken }
};
jToken = obj;
}
No idea if it is the best option ...

How to ignore empty arrays using JsonConvert.DeserializeObject?

I am reading in a list of objects from JSON using this call:
Rootobject userInfo = JsonConvert.DeserializeObject<Rootobject>(File.ReadAllText(strFileName));
But I get an exception Cannot deserialize the current JSON array. If one of the arrays within one of the class objects is empty. As long as there is data everything works.
Here is an example of JSON that is tripping up the Deserializer:
This is normal type of data for the Venue object:
"venue": {
"venue_id": 696895,
"venue_name": "Blackfinn Ameripub",
"venue_slug": "blackfinn-ameripub",
"primary_category": "Food",
"parent_category_id": "4d4b7105d754a06374d81259",
"categories": {
"count": 1,
"items": [
{
"category_name": "American Restaurant",
"category_id": "4bf58dd8d48988d14e941735",
"is_primary": true
}
]
},
"is_verified": false
},
And here is what is causing the exception, an empty array:
"venue": [
],
I have tried using the JsonSerializerSettings options including DefaultValueHandling, NullValueHandling and MissingMemberHandling but none of them seem to prevent the error.
Any idea how to deserialize the JSON and just ignore any empty arrays within the data? I'd like this to handle any empty arrays not just the example above for the object Venue.
New issue was found - 03/17/2018 <<
Hi, the converter below has been working perfectly but the server I am getting my json responses from threw another challenge. JSON.NET has had no problem retrieving this type of data:
"toasts": {
"total_count": 1,
"count": 1,
"auth_toast": false,
"items": [
{
"uid": 3250810,
"user": {
"uid": 3250810,
"account_type": "user",
"venue_details": [
],
"brewery_details": [
]
},
"like_id": 485242625,
"like_owner": false,
"created_at": "Wed, 07 Mar 2018 07:54:38 +0000"
}
]
},
Specifically the section that has venue_details. 99% of the responses come back with venue_details in this format:
"venue_details": [
],
But then I get this format suddenly:
"toasts": {
"total_count": 1,
"count": 1,
"auth_toast": false,
"items": [
{
"uid": 4765742,
"user": {
"uid": 4765742,
"account_type": "venue",
"venue_details": {
"venue_id": 4759473
},
"brewery_details": [
],
"user_link": "https://untappd.com/venue/4759473"
},
"like_id": 488655942,
"like_owner": false,
"created_at": "Fri, 16 Mar 2018 16:47:10 +0000"
}
]
},
Notice how venue_details now has a value and includes a venue_id.
So instead venue_details ends up looking like an object instead of an array. This ends up giving this exception:
JsonSerializationException: Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[System.Object]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.
In the converter code provided, that exception happens in this line with *s next to it:
public abstract class IgnoreUnexpectedArraysConverterBase : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var contract = serializer.ContractResolver.ResolveContract(objectType);
if (!(contract is JsonObjectContract))
{
throw new JsonSerializationException(string.Format("{0} is not a JSON object", objectType));
}
do
{
if (reader.TokenType == JsonToken.Null)
return null;
else if (reader.TokenType == JsonToken.Comment)
continue;
else if (reader.TokenType == JsonToken.StartArray)
{
var array = JArray.Load(reader);
if (array.Count > 0)
throw new JsonSerializationException(string.Format("Array was not empty."));
return existingValue ?? contract.DefaultCreator();
}
else if (reader.TokenType == JsonToken.StartObject)
{
// Prevent infinite recursion by using Populate()
existingValue = existingValue ?? contract.DefaultCreator();
*** serializer.Populate(reader, existingValue); ***
return existingValue;
Any ideas how to add this additional handling to account for a flip like this between the JSON returning an object instead of an array?
Thanks,
Rick
Your problem is not that you need to ignore empty arrays. If the "items" array were empty, there would be no problem:
"items": [],
Instead your problem is as follows. The JSON standard supports two types of container:
The array, which is an ordered collection of values. An array begins with [ (left bracket) and ends with ] (right bracket). Values are separated by , (comma).
The object, which is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace).
For some reason the server is returning an empty array in place of a null object. If Json.NET expects to encounter a JSON object but instead encounters a JSON array, it will throw the Cannot deserialize the current JSON array exception you are seeing.
You might consider asking whoever generated the JSON to fix their JSON output, but in the meantime, you can use the following converters to skip unexpected arrays when deserializing objects:
public class IgnoreUnexpectedArraysConverter<T> : IgnoreUnexpectedArraysConverterBase
{
public override bool CanConvert(Type objectType)
{
return typeof(T).IsAssignableFrom(objectType);
}
}
public class IgnoreUnexpectedArraysConverter : IgnoreUnexpectedArraysConverterBase
{
readonly IContractResolver resolver;
public IgnoreUnexpectedArraysConverter(IContractResolver resolver)
{
if (resolver == null)
throw new ArgumentNullException();
this.resolver = resolver;
}
public override bool CanConvert(Type objectType)
{
if (objectType.IsPrimitive || objectType == typeof(string))
return false;
return resolver.ResolveContract(objectType) is JsonObjectContract;
}
}
public abstract class IgnoreUnexpectedArraysConverterBase : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var contract = serializer.ContractResolver.ResolveContract(objectType);
if (!(contract is JsonObjectContract))
{
throw new JsonSerializationException(string.Format("{0} is not a JSON object", objectType));
}
do
{
if (reader.TokenType == JsonToken.Null)
return null;
else if (reader.TokenType == JsonToken.Comment)
continue;
else if (reader.TokenType == JsonToken.StartArray)
{
var array = JArray.Load(reader);
if (array.Count > 0)
throw new JsonSerializationException(string.Format("Array was not empty."));
return null;
}
else if (reader.TokenType == JsonToken.StartObject)
{
// Prevent infinite recursion by using Populate()
existingValue = existingValue ?? contract.DefaultCreator();
serializer.Populate(reader, existingValue);
return existingValue;
}
else
{
throw new JsonSerializationException(string.Format("Unexpected token {0}", reader.TokenType));
}
}
while (reader.Read());
throw new JsonSerializationException("Unexpected end of JSON.");
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then, if empty arrays can appear in only one place in the object graph, you can add the converter to your model as follows:
public class Rootobject
{
[JsonConverter(typeof(IgnoreUnexpectedArraysConverter<Venue>))]
public Venue venue { get; set; }
}
But if, as you say, any object might be replaced with an empty array, you can use the non-generic IgnoreUnexpectedArraysConverter for all object types:
var resolver = new DefaultContractResolver(); // Cache for performance
var settings = new JsonSerializerSettings
{
ContractResolver = resolver,
Converters = { new IgnoreUnexpectedArraysConverter(resolver) },
};
var userInfo = JsonConvert.DeserializeObject<Rootobject>(jsonString, settings);
Notes:
The converter does not work with the TypeNameHandling or PreserveReferencesHandling settings.
The converter assumes that the object being deserialized has a default constructor. It the object has a parameterized constructor you will need to create a hardcoded converter to allocate and populate the object.
The converter throws an exception if the array is not empty, to ensure there is no data loss in the event of incorrect assumptions about the structure of the JSON. Sometimes servers will write a single object in place of a one-object array, and an array when there are zero, two or more objects. If you are also in that situation (e.g. for the "items" array) see How to handle both a single item and an array for the same property using JSON.net.
If you want the converter to return a default object instead of null when encountering an array, change it as follows:
else if (reader.TokenType == JsonToken.StartArray)
{
var array = JArray.Load(reader);
if (array.Count > 0)
throw new JsonSerializationException(string.Format("Array was not empty."));
return existingValue ?? contract.DefaultCreator();
}
Working sample .Net fiddle.

WebAPI Json Formatter considers "123" as being a valid json. How to avoid it?

I have the following ApiController method
public async Task<HttpResponseMessage> SetData(string user, [FromBody] JToken jsonPayload)
{
//
}
I'm doing some testing with malformed json values:
var malformedData = new List<string>
{
#"abcd {'value' : 0}",
#"{'abc' : }",
#"{'value' : value }",
#"{'value' : 0} abc",
"abc",
"123"
};
For all of these values I get in the controller jsonPayload being null. That's fine.
The only exception is the last string in the list 123, and the jsonPayload actually contains 123.
My Formatters config is:
config.Formatters.Clear();
config.Formatters.Add(new JsonMediaTypeFormatter());
config.Formatters.JsonFormatter.SerializerSettings.CheckAdditionalContent = true;
List<MediaTypeHeaderValue> unwantedTypes = config.Formatters.JsonFormatter.SupportedMediaTypes.Where(type => type.MediaType != "application/json").ToList();
foreach (MediaTypeHeaderValue unwantedType in unwantedTypes)
{
config.Formatters.JsonFormatter.SupportedMediaTypes.Remove(unwantedType);
}
What else can I set so that WebAPI considers this as a malformed json?

JSON.NET deserialize part of JSON object as dictionary

this is not a dup of JSON.NET: Deserializing part of a JSON object to a dictionary
I have JSON
{
"Count" : 2,
"Head" : { "itemId":..., "quality":... },
"Legs" : { "itemId":..., "quality":... }
}
I need to deserialize it into a dictionary: IDictionary<GearSlot, Item>
If I try to deserialize whole JSON I get an error because it fails to deserialize "Count:2" into <GearSlot, Item>.
GearSlot is an enum, and has ~17+ values. At this time I have defined class:
public class Items {
property Item Head { get; set; }
property Item Legs { get; set; }
...
}
it works, but not elegant and clunky.
Any suggestions?
Per this answer and this answer, you could deserialize a partial JSON fragment and then reintegrate.
var root = JObject.Parse(jsonString);
var serializer = new JsonSerializer();
var expectedResult = serializer.Deserialize<ExpectedType>(root["fragment"].CreateReader());
So for you
var items = new Items {
Head = serializer.Deserialize<Item>(root["Head"])
, Legs = serializer.Deserialize<Item>(root["Legs"])
};
You can try using a customer converter. Example: http://weblogs.asp.net/thangchung/archive/2010/08/26/customizing-the-converter-for-json-net.aspx

Resources