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 -->}}
Related
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.
I'm trying to Deserialize data from binance API. The format in the website is:
{
"lastUpdateId": 82930322,
"bids": [
["0.09766700","12.64700000",[]],
["0.09766600","0.19500000",[]],
["0.09765800","0.30300000",[]],
["0.09765600","3.50000000",[]],
["0.09765500","0.14900000",[]]
],
I try to save the data to:
public string NameOfCoin { get; set; }
public string[][] bids { get; set; }
And I get exception that it can't read the [] in the end of the array. I tryed also for a diffrent format like float or string withour array and it dosent work.
Well, the simplest solution is to change the type of your bids property from string[][] to object[][]. That will allow the deserialization to succeed, but working with the bids array will be awkward. You will have to do type checking on the items and cast them appropriately when you use them.
A better idea is to filter out the unwanted empty array values during deserialization. You can do that with a custom JsonConverter (assuming you are using Json.Net -- your question did not indicate what JSON serializer you are using). Here is one that should do the job:
class CustomConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray rows = JArray.Load(reader);
foreach (JArray row in rows.Children<JArray>())
{
foreach (JToken item in row.Children().ToList())
{
if (item.Type != JTokenType.String)
item.Remove();
}
}
return rows.ToObject<string[][]>(serializer);
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return false;
}
}
To use the converter, mark the bids property in your class with a [JsonConverter] attribute like this:
[JsonConverter(typeof(CustomConverter))]
public string[][] bids { get; set; }
Then you can deserialize to your class as usual and it should "just work".
Working demo: https://dotnetfiddle.net/TajQt4
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() );
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
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));
}
}
}