JSON.NET: Serialize json string property into json object - c#

Is it possible to tell JSON.NET I have a string with JSON data? E.g. I have a class like this:
public class Foo
{
public int Id;
public string RawData;
}
which I use like this:
var foo = new Foo();
foo.Id = 5;
foo.RawData = #"{""bar"":42}";
which I want to be serialized like this:
{"Id":5,"RawData":{"bar":42}}
Basically I have a piece of unstructured variable-length data stored as JSON already, I need fully serialized object to contain this data as a part.
Thanks.
EDIT: Just to make sure it is understood properly, this is one-way serialization, i.e. I don't need it to deserialize back into same object; the other system shall process this output. I need content of RawData to be a part of JSON, not a mere string.

You need a converter to do that, here is an example:
public class RawJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
writer.WriteRawValue(value.ToString());
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return typeof(string).IsAssignableFrom(objectType);
}
public override bool CanRead
{
get { return false; }
}
}
Then decorate your class with it:
public class Foo
{
public int Id;
[JsonConverter(typeof(RawJsonConverter))]
public string RawData;
}
Then, when you use:
var json = JsonConvert.SerializeObject(foo,
new JsonSerializerSettings());
Console.WriteLine (json);
This is your output:
{"Id":5,"RawData":{"bar":42}}
Hope it helps
Edit: I have updated my answer for a more efficient solution, the previous one forced you to serialize to then deserialize, this doesn't.

It is possible, that using JRaw could be more suitable and compact solution
See this post

You can use another property to deserialize the object property json.
public class Foo
{
public int Id;
public string RawData;
private object thisObject;
public object ThisObject
{
get
{
return thisObject ?? (thisObject = JsonConvert.DeserializeObject<object>(RawData));
}
}
}

Related

Nested JObjects getting serialized as empty arrays

I'm getting a really strange situation where I'm trying to serialize an object returned by a third party API into JSON. I don't have any control over the third party API or the object it returns. The C# POCO I'm trying to serialize looks something like this:
public class JobSummary {
public Job Job { get; set; }
}
public class Job {
public Status Status { get; set; }
}
public class Status {
public object JobOutput { get; set; }
public int Progress { get; set; }
}
Based on what the third party library returns, I would expect it to serialize to the following. At runtime, I can tell that the type of JobOutput is a JObject that contains a single key (Count) and value (0).
{
job: {
status: {
jobOutput: {
Count: 0
},
progress: 100
}
}
}
In this, job and status are obviously objects. progress is an int and jobOutput is a JObject.
If I run any of the following variations:
JToken.FromObject(jobSummary)
JObject.FromObject(jobSummary)
JObject.Parse(jobSummary)
And ToString() or JsonConvert.SerializeObject() the result, I get the following output:
{
job: {
status: {
jobOutput: {
Count: []
},
progress: 100
}
}
}
Notice that Count has become an [].
But if I do jobSummary.Status.JobOutput.ToString(), I correctly get back 0, so I know that the POCO returned by the third party library isn't malformed and has the info I need.
Does anybody know what could be going on? Or how I can correctly serialize the nested JObject?
Edit: I should clarify that I'm on v6.0.8 of Newtonsoft for reasons outside my control, and that the thirdparty assembly that contains the POCO has an unknown version of Newtonsoft ILMerged in it. I don't know if that is relevant.
You wrote that
I should clarify that I'm on v6.0.8 of Newtonsoft for reasons outside my control, and that the thirdparty assembly that contains the POCO has an unknown version of Newtonsoft ILMerged in it.
This explains your problem. The JobOutput contains an object with full name Newtonsoft.Json.Linq.JObject from a completely different Json.NET DLL than the one you are using. When your version of Json.NET tests to see whether the object being serialized is a JToken, it checks objectType.IsSubclassOf(typeof(JToken)) -- which will fail since the ILMerged type is not, in fact, a subclass of your version's type, despite having the same name.
As a workaround, you will need to create custom JsonConverter logic that uses the ToString() methods of the foreign JToken objects to generate output JSON, then writes that JSON to the JSON stream you are generating. The following should do the job:
public class ForeignJsonNetContainerConverter : ForeignJsonNetBaseConverter
{
static readonly string [] Names = new []
{
"Newtonsoft.Json.Linq.JObject",
"Newtonsoft.Json.Linq.JArray",
"Newtonsoft.Json.Linq.JConstructor",
"Newtonsoft.Json.Linq.JRaw",
};
protected override IReadOnlyCollection<string> TypeNames { get { return Names; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var json = value.ToString();
// Fix indentation
using (var stringReader = new StringReader(json))
using (var jsonReader = new JsonTextReader(stringReader))
{
writer.WriteToken(jsonReader);
}
}
}
public class ForeignJsonNetValueConverter : ForeignJsonNetBaseConverter
{
static readonly string [] Names = new []
{
"Newtonsoft.Json.Linq.JValue",
};
protected override IReadOnlyCollection<string> TypeNames { get { return Names; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var underlyingValue = ((dynamic)value).Value;
if (underlyingValue == null)
{
writer.WriteNull();
}
else
{
// JValue.ToString() will be wrong for DateTime objects, we need to serialize them instead.
serializer.Serialize(writer, underlyingValue);
}
}
}
public abstract class ForeignJsonNetBaseConverter : JsonConverter
{
protected abstract IReadOnlyCollection<string> TypeNames { get; }
public override bool CanConvert(Type objectType)
{
if (objectType.IsPrimitive)
return false;
// Do not use the converter for Native JToken types, only non-native types with the same name(s).
if (objectType == typeof(JToken) || objectType.IsSubclassOf(typeof(JToken)))
return false;
var fullname = objectType.FullName;
if (TypeNames.Contains(fullname))
return true;
return false;
}
public override bool CanRead { get { return false; } }
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And then use them in settings as follows:
var settings = new JsonSerializerSettings
{
Converters =
{
new ForeignJsonNetContainerConverter(), new ForeignJsonNetValueConverter()
},
};
var json = JsonConvert.SerializeObject(summary, Formatting.Indented, settings);
Notes:
The converters work by assuming that types whose FullName matches a Json.NET type's name are, in fact, Json.NET types from a different version.
JValue.ToString() returns localized values for DateTime objects (see here for details), so I created a separate converter for JValue.
I also fixed the indentation to match.
Mockup fiddle here.

Deserialize array within array

I just downloaded a huge JSON file with all current MTG sets/cards, and am looking to deserialize it all.
I've gotten most of each set deserialized, but I'm running into a snag when trying to deserialize the booster object within each Set:
As you can see from the above two pictures, in each booster object there is a list of strings, but for some booster objects there is also an additional array of more strings. Deserializing an array of exclusively strings isn't a problem. My issue arises when I run into those instances where there is an array of strings within the booster object that need deserializing.
Currently the property I have set up to handle this deserialization is:
public IEnumerable<string> booster { get; set; }
But when I run into those cases where there's another array within booster I get an exception thrown, where Newtonsoft.Json complains it doesn't know how to handle the deserialization.
So, my question then becomes: how can I go about deserializing an array of strings contained within an array of strings? And what would an object need to look like in C# code to handle that sort of deserialization?
You could deserialize the per item as string[] even thought the item wouldn't be a collection. So, provide a custom serializer;
public class StringArrayConverter : JsonConverter
{
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)
{
JArray array = JArray.Load(reader);
for (int i = 0; i < array.Count; i++)
{
//If item is just a string, convert it string collection
if (array[i].Type == JTokenType.String)
{
array[i] = JToken.FromObject(new List<string> {array[i].ToObject<string>()});
}
}
return array.ToObject<List<string[]>>();
}
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<string[]>));
}
}
public class JsonObject
{
[JsonConverter(typeof(StringArrayConverter))]
public List<string[]> booster { get; set; }
}
Then deserialize the json;
var data = JsonConvert.DeserializeObject<JsonObject>(json);
Finally, you can deserialize a json like I provided below;
{
"booster": [
"1",
"2",
["3","4"]
]
}
If you are using C# as your programming language then use the below link to generate C# class from JSON string
http://json2csharp.com/
You can then use the generated class in your code to deserialize your json string to object using JsonConvert.DeserializeObject(jssonstring)
The easiest solution would be to change the type to IEnumerable<object>. So it can store string or string[].
Or you could create a class Item with two properties of types string and string[]. Then you could create another property that returns the one that's not null, so now instead of the whole item being an object, you can have a special class that only returns one of two types so you can be sure that you get either a string or a string[]. Hope that makes sense.
Your property: public IEnumerable<Item> booster { get; set; }
public class Item
{
private string _text;
private string[] _array;
public object Value => (object)_text ?? (object)_array;
public Item(string text) { _text = text; }
public Item(string[] array) { _array = array; }
}
When you need to use this value, you can check if it's string or string[] like this:
if(myItem is string text)
{
// operate on variable text
}
else // you can be sure that myItem is of type string[] because we covered this up in the Item class
{
var array = (string[])myItem;
// operate on variable array
}
Another option would be to model "booster" as IEnumerable<string[]> and then use a custom JsonConverter to force strings to arrays. In the process of testing this theory, I wrote a (minimally tested, but functional) converter for you :)
public class ForceStringToArrayConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(IEnumerable<string[]>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var listObject = new List<string[]>();
var jObject = JToken.Load(reader);
foreach (JToken token in jObject.Children())
{
if (token.Type == JTokenType.Array)
{
var arrayObj = (JArray)token;
listObject.Add(arrayObj.ToObject<string[]>());
}
else if (token.Type == JTokenType.String)
{
listObject.Add(new string[] { token.ToString() });
}
}
return listObject;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then when you invoke DeserializeObject, pass it your custom converter:
var obj = Newtonsoft.Json.JsonConvert.DeserializeObject<YourTypeHere>(testJson, new ForceStringToArrayConverter() );

How can I remove quotes from serilaized string?

I need to produce a JSON document, that will be parsed by a SSI mechanism on a device. The document will actually be a json serialized dictionary. For the sake of simplicity, let's say, that it should look like this:
var x = new Dictionary<string,object>
{
["A"]=new {x = "<!-- ?A.x -->"},
["B"]=new {x = "<!-- ?B.x -->"}
};
JsonConvert.SerializeObject(x).Dump();
Which produces in LinqPad:
{"A":{"x":"<!-- ?A.x -->"},"B":{"x":"<!-- ?B.x -->"}}
But actually those "x" fields are numbers, and when fetched from the device, they will contain numbers. So I would need to serialize this dictionary without quotes around a field value that is string on C# side:
{"A":{"x":<!-- ?A.x -->},"B":{"x":<!-- ?B.x -->}}
How can I force Newtonsoft Json.NET serializer not to add quotes to the value of specific fields (not all) during serialization?
Thank you.
One way to do it is by introducing new JsonConverter (sample). To separate the functionality of "raw serialization", you could introduce new type that would just wrap a string value, eg.
public class RawJson
{
public string Value { get; private set; }
public RawJson(string value)
{
Value = value;
}
}
Then you just check for this type in converter's CanConvert() and in WriteJson() you can just write
writer.WriteRawValue(((RawJson)value).Value);
And below is the actual solution, based on #kiziu's suggestion to use custom converter. But without custom type. As the converter can be added with the attribute to members too, and not only to classes or the converter itself, I can use it on the property I need. The above LinqPad scratch updated:
internal class RawJsonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(string);
}
public override bool CanRead
{
get { return false; }
}
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)
{
writer.WriteRawValue((string)value);
}
}
class myClass
{
[JsonConverter(typeof(RawJsonConverter))]
public string x;
}
void Main()
{
var x = new Dictionary<string,object>
{
["A"]=new myClass {x = "<!-- ?A.x -->"},
["B"]=new myClass {x = "<!-- ?B.x -->"}
};
JsonConvert.SerializeObject(x).Dump();
}
And the result is, as expected:
{"A":{"x":<!-- ?A.x -->},"B":{"x":<!-- ?B.x -->}}

Serialize only derived type using Newtonsoft Json.NET

Is there any JsonSerializerSettings available for serializing only the derived type.
for example consider i have below two class. When I am serializing Employee object the the result json should only contains the properties of employee not the person class.
public class Person
{
public string Name { get; set; }
}
public class Employee : Person
{
public DateTime JoiningDate { get; set; }
public string EmployeeId { get; set;}
}
Questions like those usually reflect an issue with model design, however, one way to do what you want to do is to get rid of the inheritance; you could try something like converting your object to dynamic and then serialize the dynamic object :
class MyJson
{
public static string SerializeObject<T>(T obj, bool ignoreBase)
{
if (!ignoreBase)
{
return JsonConvert.SerializeObject(obj);
}
var myType = typeof(T);
var props = myType.GetProperties().Where(p => p.DeclaringType == myType).ToList();
var x = new ExpandoObject() as IDictionary<string, Object>;
props.ForEach(p => x.Add(p.Name, p.GetValue(obj, null)));
return JsonConvert.SerializeObject(x);
}
}
call it like
MyJson.SerializeObject<Employee>(e, true)
This way you can use it for any type and filter the properties to serialize however you wish in the helper class. For example, you can check the property attributes and decided if it should be added to the dynamic object.
You can use a custom JsonConverter for that purpose. Please see below for a basic version for your purpose.
public class PersonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (objectType == typeof(Employee))
return true;
return false;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
return "";
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
}
else
{
JObject o = (JObject)t;
o.Remove("Name"); //currently the property name is hardcoded. You could enhance this to remove any property you like
o.WriteTo(writer);
}
}
}
After creating your JsonConverter you can use that during deserialization like below,
var des = JsonConvert.SerializeObject(e, new PersonConverter());
//e is the Person instance we want to convert and PersonConverter is the custom converter
//we use of serialization
Please see this link for more information on this

JSON.NET Deserialization - Single Result vs Array

I am having trouble trying to determine how to make my Serialization Properly be able to access a single result, as well as an array.
When I make a REST call looking for something on a server, sometimes it will return an Array of models, but if the search results only have a single model, it will not be returned as an error. This is when I get an exception that I cannot deserialize because the Object Property is expecting an array, but is instead receiving a single object.
Is there a way to define my class so that it can handle a single object of type ns1.models when that is returned instead of an array of objects?
[JsonObject]
public class Responses
{
[JsonProperty(PropertyName = "ns1.model")]
public List<Model> Model { get; set; }
}
Response that can be deserialized:
{"ns1.model":[
{"#mh":"0x20e800","ns1.attribute":{"#id":"0x1006e","$":"servername"}},
{"#mh":"0x21a400","ns1.attribute":{"#id":"0x1006e","$":"servername2"}}
]}
Response that cannot be serialized (because JSON includes only a singe "ns1.model"):
{"ns1.model":
{"#mh":"0x20e800","ns1.attribute":{"#id":"0x1006e","$":"servername"}}
}
Exception:
Newtonsoft.Json.JsonSerializationException was unhandled HResult=-2146233088 Message=Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[ConsoleApplication1.Model]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path '['ns1.model-response-list'].['ns1.model-responses'].['ns1.model'].#mh', line 1, position 130
To handle this you have to use a custom JsonConverter. But you probably already had that in mind.
You are just looking for a converter that you can use immediately. And this offers more than just a solution for the situation described.
I give an example with the question asked.
How to use my converter:
Place a JsonConverter Attribute above the property. JsonConverter(typeof(SafeCollectionConverter))
public class Response
{
[JsonProperty("ns1.model")]
[JsonConverter(typeof(SafeCollectionConverter))]
public List<Model> Model { get; set; }
}
public class Model
{
[JsonProperty("#mh")]
public string Mh { get; set; }
[JsonProperty("ns1.attribute")]
public ModelAttribute Attribute { get; set; }
}
public class ModelAttribute
{
[JsonProperty("#id")]
public string Id { get; set; }
[JsonProperty("$")]
public string Value { get; set; }
}
And this is my converter:
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
namespace stackoverflow.question18994685
{
public class SafeCollectionConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
//This not works for Populate (on existingValue)
return serializer.Deserialize<JToken>(reader).ToObjectCollectionSafe(objectType, serializer);
}
public override bool CanWrite => false;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}
And this converter uses the following class:
using System;
namespace Newtonsoft.Json.Linq
{
public static class SafeJsonConvertExtensions
{
public static object ToObjectCollectionSafe(this JToken jToken, Type objectType)
{
return ToObjectCollectionSafe(jToken, objectType, JsonSerializer.CreateDefault());
}
public static object ToObjectCollectionSafe(this JToken jToken, Type objectType, JsonSerializer jsonSerializer)
{
var expectArray = typeof(System.Collections.IEnumerable).IsAssignableFrom(objectType);
if (jToken is JArray jArray)
{
if (!expectArray)
{
//to object via singel
if (jArray.Count == 0)
return JValue.CreateNull().ToObject(objectType, jsonSerializer);
if (jArray.Count == 1)
return jArray.First.ToObject(objectType, jsonSerializer);
}
}
else if (expectArray)
{
//to object via JArray
return new JArray(jToken).ToObject(objectType, jsonSerializer);
}
return jToken.ToObject(objectType, jsonSerializer);
}
public static T ToObjectCollectionSafe<T>(this JToken jToken)
{
return (T)ToObjectCollectionSafe(jToken, typeof(T));
}
public static T ToObjectCollectionSafe<T>(this JToken jToken, JsonSerializer jsonSerializer)
{
return (T)ToObjectCollectionSafe(jToken, typeof(T), jsonSerializer);
}
}
}
What does it do exactly?
If you place the converter attribute the converter will be used for this property. You can use it on a normal object if you expect a json array with 1 or no result. Or you use it on an IEnumerable where you expect a json object or json array. (Know that an array -object[]- is an IEnumerable)
A disadvantage is that this converter can only be placed above a property because he thinks he can convert everything. And be warned. A string is also an IEnumerable.
And it offers more than an answer to the question:
If you search for something by id you know that you will get an array back with one or no result.
The ToObjectCollectionSafe<TResult>() method can handle that for you.
This is usable for Single Result vs Array using JSON.net
and handle both a single item and an array for the same property
and can convert an array to a single object.
I made this for REST requests on a server with a filter that returned one result in an array but wanted to get the result back as a single object in my code. And also for a OData result response with expanded result with one item in an array.
Have fun with it.
I think your question has been answered already. Please have a look at this thread:
How to handle both a single item and an array for the same property using JSON.net .
Basically the way to do it is to define a custom JsonConvertor for your property.
There is not an elegant solution to your problem in the current version of JSON.NET. You will have to write custom parsing code to handle that.
As #boyomarinov said you can develop a custom converter, but since your JSON is pretty simple you can just parse your JSON into an object and then handle the two cases like this:
var obj = JObject.Parse(json);
var responses = new Responses { Model = new List<Model>() };
foreach (var child in obj.Values())
{
if (child is JArray)
{
responses.Model = child.ToObject<List<Model>>();
break;
}
else
responses.Model.Add(child.ToObject<Model>());
}
Use JRaw type proxy property ModelRaw:
public class Responses
{
[JsonIgnore]
public List<Model> Model { get; set; }
[JsonProperty(PropertyName = "ns1.model")]
public JRaw ModelRaw
{
get { return new JRaw(JsonConvert.SerializeObject(Model)); }
set
{
var raw = value.ToString(Formatting.None);
Model = raw.StartsWith("[")
? JsonConvert.DeserializeObject<List<Model>>(raw)
: new List<Model> { JsonConvert.DeserializeObject<Model>(raw) };
}
}
}

Categories

Resources