Using ServiceStack's JsonSerializer to serialize arrays containing some null items - c#

In ServiceStack 3.9, when deserializing a JSON array that contains some nulls, the null values are deserialized as nulls, as I expected. However, when I then serialize the same array back to JSON again, the nulls turn into empty objects.
public class MyClass
{
public string Foo { get; set; }
}
[Fact]
public void Test()
{
var originalJson = "[{\"Foo\":\"Bar\"},null]";
var arr = ServiceStack.Text.JsonSerializer.DeserializeFromString<MyClass[]>(originalJson);
var result = ServiceStack.Text.JsonSerializer.SerializeToString(arr);
// output is actually: [{"Foo":"Bar"},{}]
Assert.Equal(originalJson, result); // fails
}
Is this expected behavior? Or is there another way to serialize an array containing some nulls, and have the null items appear in JSON as nulls rather than as empty objects?
Note that when serializing an array of literals, like strings, null values are returned as null values, and not as objects.

I had this problem too. I found that if the array is cast to object[] before calling SerializeToString() then the null values are output as expected.
var result = ServiceStack.Text.JsonSerializer.SerializeToString((object[])arr);
// result is correct [{"Foo":"Bar"}, null]
You can globally set the serialization of MyClass using this JsConfig:
JsConfig<MyClass[]>.RawSerializeFn = (obj) => ((object[])obj).ToJson();

Related

In C# deserialize simple JSON object into a list of integers

I keep finding examples of more complex JSON data and so far nothing for such a simple object. (note: this is .NET not Javascript)
{"Ids":[5,6,7,8,9,10]}
I don't want to create a class, all I want is a simple List a simple list of the Ids so that I can loop over them later in the code.
This does not work:
int[] ids = JsonConvert.DeserializeObject<int[]>(message.Message);
Nor does this:
List<int> ids = JsonConvert.DeserializeObject<List<int>>(message.Message);
I get errors like "Cannot deserialize the current JSON object ... because the type requires a JSON array..."
Neither int[] nor List<int> represent your json structure, you need to introduce a class to hold Ids array property:
public class Root
{
public List<int> Ids { get; set; }
}
And deserialize to it:
var ids = JsonConvert.DeserializeObject<Root>(message.Message);
Another option is to parse your json as JObject and deserialize property value:
var ids = JObject.Parse(message.Message)["Ids"].ToObject<int[]>();

Serializing plain values and objects with JObject.FromObject

I have problem with serializing objects using Newtonsoft.JSON. I have a method which creates EventGridEvent object:
public EventGridEvent CreateEvent(object data) =>
new EventGridEvent
{
Id = Guid.NewGuid().ToString(),
EventTime = DateTime.Now,
Data = JObject.FromObject(data, JsonSerializer),
...other properties
}
When the method is called with "proper" object, everything serializes properly. The problem is if the data is a plain value i.e. integer or string. In this case I get an exception Object serialized to Integer. JObject instance expected. How to detect if object can be serialized with JObject.FromObject and if not use its plain value (without using try/catch)?
If EventGridEvent.Data can hold any type of JSON value, you should modify it to be of type JToken and use JToken.FromObject(Object, JsonSerializer), i.e.:
public class EventGridEvent
{
public JToken Data { get; set; }
// ...other properties
}
And then do
Data = JToken.FromObject(data, JsonSerializer),
A JToken
Represents an abstract JSON token.
It can be used to hold the JSON representation of any JSON type including an array, object, or primitive value. See: JSON.NET: Why Use JToken--ever?.
If you need to serialize a nullable value like an int? or string, JToken.FromObject() will throw an error. I've found that the implicit operator (called by casting) works better.
string s = null;
int? i = null;
JObject obj = new JObject();
// obj.Add(JToken.FromObject(s)); // throws exception
// obj.Add(JToken.FromObject(i)); // throws exception
obj.Add((JToken)s); // works
obj.Add((JToken)i); // works

Conditionally deserialize JSON string or array property to C# object using JSON.NET? [duplicate]

This question already has answers here:
How to handle both a single item and an array for the same property using JSON.net
(9 answers)
Closed 6 years ago.
I have a defined C# object based off a very complex JSON I'm getting from a third party API. Here's a piece of it:
{"alarmSummary":"NONE"}
The corresponding property in C# would be:
public string alarmSummary {get; set;}
And I would get this converted by using my typical JSONConvert from JSON.NET:
var alarms = JSONConvert.DeserializeObject<MyClass>(jsonString);
However, the API will put alarms in this format, as an array, and "NONE" when there aren't any as a string:
{"alarmSummary" : ["AHHH Something went wrong!!", "I'm on fire!!"]}
Which means in C# it would need to be:
public string[] alarmSummary {get; set;}
If I could, I would just have the JSONConvert deserialize the "NONE" string into an array of just one entry. Is there a way to set conditions on this property, or will I have to take the whole complex JSON string and hand convert it?
This one should be easy - you can set your alarmSummary as object and then have separate property that evaluates alarmSummary and returns null or array depending on the value.
public object alarmSummary { get; set; }
protected string[] alarmSummaryCasted
{
get
{
if (alarmSummary is String)
return null;
else
return (string[]) alarmSummary;
}
}
If you are expecting only those two combinations, you could make use of the dynamic keyword and check deserialized object:
string json = "{\"alarmSummary\":\"NONE\"}";
//string json = "{\"alarmSummary\" : [\"AHHH Something went wrong!!\", \"I'm on fire!!\"]}";
string[] alarmSummary;
dynamic obj = JsonConvert.DeserializeObject(json);
if (obj.alarmSummary.Type == JTokenType.Array)
alarmSummary = obj.alarmSummary.ToObject<string[]>();
else
alarmSummary = new[] { (string)obj.alarmSummary.ToObject<string>() };

How to get object json when deserializing array

I have an incoming JSON, which consists array of some objects, say, Foo. I deserialize them with
result = JsonConvert.DeserializeObject<List<Foo>>(message);
Now i want to add a string property to Foo, which will store it's JSON (which i received), so that Foo'll look like:
public class Foo
{
public int MyInt { get; set; }
public bool MyBool { get; set; }
public string JSON { get; set; }
}
But i don't know how can i say JSON.Net the way it can populate such a field..
UPD
I'll clearify what i want. Say i receive JSON:
[{"MyInt":1,"MyBool":0},{"MyInt":2,"MyBool":0},{"MyInt":3,"MyBool":1}]
Here is array of 3 objects and i want, when deserializing, to add corresponding part of json to object, so that:
First object will contain {"MyInt":1,"MyBool":0}
Second object will contain {"MyInt":2,"MyBool":0}
Third object will contain {"MyInt":3,"MyBool":1}
in their JSON Property
I'll be gratefull for any help!
This is one way to do it, but it doesn't maintain the exact original JSON - but it does provide a static record of the original JSON (but without the exact format of the original values - i.e. Bool maybe be 0/1 or true/false):
var message = #"[{""MyInt"":1,""MyBool"":0},{""MyInt"":2,""MyBool"":0},{""MyInt"":3,""MyBool"":1}]";
var foos = JsonConvert.DeserializeObject<List<Foo>>(message);
var t = JsonConvert.SerializeObject(foos[0]);
foos = foos.Select(s => new Foo() { MyBool = s.MyBool, MyInt = s.MyInt, JSON = JsonConvert.SerializeObject(s) }).ToList();
If you are dealing with a lot of Foos, then you might want to find a more efficient way. There might be a way to 'update' using linq, rather than creating a new list.
Okay, i found an answer. I didn't know that i can deserialize message into JArray and then enumerate it (good job, newtonsoft:) ). Here is what i endede up with:
if (tokenType is JArray)
{
var arr = JsonConvert.DeserializeObject(message) as JArray;
foreach (var item in arr)
{
try
{
var agentParameter = item.ToObject<Foo>();
agentParameter.JSON = item.ToString();
result.Add(agentParameter);
}
catch (Exception)
{
LogProvider.Error(string.Format("Failed to Deserialize message. Message text: \r\n {0}", item.ToString()));
}
}
}

Determining object "type" during json deserialization

While trying to de-serialize a complex JSON object (JIRA issue) into an object containing a dictionary of type string-Field I've hit a bit of a bump.
While I can de-serialize various pre-determined object types (standard), I'm having a bit of a harder time with the custom fields, which could be of various types (they all begin with customfield_ followed by a set of numbers).
The custom fields can be floats, strings, booleans, objects and arrays of objects. The latter of these is causing me issues since I can't seem to determine what the object is before I de-serialize it.
I've searched for a way to perhaps "peek" at the data in the object before de-serializing as one of the fields contains information specific to it's type. This is all so I can determine the type of the object and tell Json.Net what to de-serialize it as.
I've considered parsing the JSON string before serialization to get the information, or maybe just when hitting this particular case, but maybe there is a better way?
Thanks in advance for any advice on this.
You can deserialize to an object with Json.Net. Here's a quick and dirty example:
using System;
using Newtonsoft.Json;
namespace Sandbox
{
class Program
{
private static void Main(string[] args)
{
var nestDto = new Dto
{
customfield_1 = 20,
customfield_2 = "Test2"
};
var dto = new Dto
{
customfield_1 = 10,
customfield_3 = new[] { nestDto },
customfield_2 = "Test"
};
var jsonString = JsonConvert.SerializeObject(dto);
Console.WriteLine(jsonString);
var fromJsonString = JsonConvert.DeserializeObject<Dto>(jsonString);
Console.WriteLine(fromJsonString.customfield_3[0].customfield_2); //Outputs Test2
Console.ReadKey();
}
}
class Dto
{
public int customfield_1 { get; set; }
public string customfield_2 { get; set; }
public Dto[] customfield_3 { get; set; }
}
}
Instead of peaking, you can deserialize as the same type as JSON.net uses for ExtensionData explicitly. For example:
if (reader.TokenType == JsonToken.StartArray)
{
var values = serializer.Deserialize<List<Dictionary<string, JToken>>>(reader);
objectContainer = ClassifyAndReturn(values);
}
private ObjectType ClassifyAndReturn(List<Dictionary<string, JToken>> values)
{
if (values.First().ContainsKey("self"))
{
string self = values.First()["self"].Value<string>();
if (self.Contains("customFieldOption"))
//... Then go into a series of if else cases to determine the object.
The representation of the objects are given as a Dictionary of string to JToken, which can then easily be checked and assigned manually or in some cases automatically deserialized (in the case one of the fields is another object).
Here is what an object constructor could look like:
internal myobject(Dictionary<string, JToken> source)
{
Self = source["self"].Value<string>();
Id = source["id"].Value<string>();
Value = source["value"].Value<string>();
}

Categories