JSON.net - field is either string or List<string> - c#

I have a situation where the JSON returned from a REST-service returns a list of Movie-objects, all specced out with a ton of information. A couple of fields in that REST-service result changes depending on the information available.
An example: A Movie always has some screen captures (images), actors and directors. Depending on the movie in question, there might be one or more images, one or more actors and one or more directors. Sample JSON for a couple of cases:
{
"title": "Movie title",
"images": [
"http://www.url.com/img_0.jpg",
"http://www.url.com/img_1.jpg",
"http://www.url.com/img_2.jpg",
"http://www.url.com/img_3.jpg",
"http://www.url.com/img_4.jpg"
],
"actors": [
"Steven Berkoff",
"Nikolaj Coster-Waldau",
"Julie Cox"
],
"directors": "Simon Aeby"
},
{
"title": "Another movie",
"images": "http://www.url.com/img_1.jpg",
"actors": "actor 1"
"directors": [
"Justin Bieber",
"Justin Timberlake"
]
}
The question is, using JSON.net, how can I create a converter that deals with this problem? I've been scouring the internet, but still haven't found a solution.
Another spin on the same question: If a field is either a List of strings or a simple string, how do I make JSON.NET create a List either way (and if just a simple string, create a list with one member)
EDIT: This REST-service is out of my control

Ok, I did it for fun, but don't think is useful or the best way, anyway...
Declaring the "dynamic" attributes as object and then create methods to obtain the properties as something like ImagesAsList or ImagesAsString. I did it with Extension Methods.....
var movies = JsonConvert.DeserializeObject<List<Movie>>(str);
Class
class Movie
{
[JsonProperty("title")]
public string Title { get; set; }
[JsonProperty("images")]
public object Images { get; set; }
[JsonProperty("actors")]
public object Actor { get; set; }
[JsonProperty("directors")]
public object Directors { get; set; }
}
Extension Methods
static class MovieExtension
{
public static List<string> ImagesAsList(this Movie m)
{
var jArray = (m.Images as JArray);
if (jArray == null) return null;
return jArray.Select(x => x.ToString()).ToList();
}
public static string ImagesAsString(this Movie m)
{
return m.Images as string;
}
}
EDIT
After reading #yamen comments I did some changes like:
var settings = new JsonSerializerSettings();
settings.Converters.Add(new MoviesConverter());
var movies = JsonConvert.DeserializeObject<List<Movie>>(str, settings);
Class
class Movie
{
[JsonProperty("title")]
public List<string> Title { get; set; }
[JsonProperty("images")]
public List<string> Images { get; set; }
[JsonProperty("actors")]
public List<string> Actor { get; set; }
[JsonProperty("directors")]
public List<string> Directors { get; set; }
}
Converter
class MoviesConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(string)) || (objectType == typeof(List<string>)) ;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
var l = new List<string>();
reader.Read();
while (reader.TokenType != JsonToken.EndArray)
{
l.Add(reader.Value as string);
reader.Read();
}
return l;
}
else
{
return new List<string> { reader.Value as string };
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
//ToDo here we can decide to write the json as
//if only has one attribute output as string if it has more output as list
}
}

You won't be able to serialise directly to an object, but you can do so manually without too much effort. JSON.Net contains LINQ to JSON. First define a method that will always return a list of type T even if the underlying JSON is not an array:
public List<T> getSingleOrArray<T>(JToken token)
{
if (token.HasValues)
{
return token.Select(m => m.ToObject<T>()).ToList();
}
else
{
return new List<T> { token.ToObject<T>() };
}
}
Sample usage:
JObject m1 = JObject.Parse(#"{
""title"": ""Movie title"",
""images"": [
""http://www.url.com/img_0.jpg"",
""http://www.url.com/img_1.jpg""
],
""actors"": [
""Steven Berkoff"",
""Julie Cox""
],
""directors"": ""Simon Aeby""
}");
JObject m2 = JObject.Parse(#"{
""title"": ""Another movie"",
""images"": ""http://www.url.com/img_1.jpg"",
""actors"": ""actor 1"",
""directors"": [
""Justin Bieber"",
""Justin Timberlake""
]
}");
IList<String> m1_directors = getSingleOrArray<string>(m1["directors"]);
IList<String> m2_directors = getSingleOrArray<string>(m2["directors"]);
m1_directory is a list with a single element, m2_directors is a list with two elements.

Related

C# Deserialize JSON array with multiple objects

I'm taking in a JSON that I do not control in the format of this sample:
{
"Transaction Information": [
{
"Type": "This is the Type"
},
{
"Action": "No"
},
{
"Owner": "Simpsons"
},
{
"Buyer/Broker": "Y"
},
{
"Compensation to Buyer": 3.0
}
]
}
I want to deserialize it to a class such as:
public class Transaction
{
[JsonProperty("Transaction Information")]
public TransactionInformation[] TransactionInformation { get; set; }
}
public partial class TransactionInformation
{
[JsonProperty("Type", NullValueHandling = NullValueHandling.Ignore)]
public string Type { get; set; }
[JsonProperty("Action", NullValueHandling = NullValueHandling.Ignore)]
public string Action { get; set; }
[JsonProperty("Owner", NullValueHandling = NullValueHandling.Ignore)]
public string Owner { get; set; }
[JsonProperty("Buyer/Broker", NullValueHandling = NullValueHandling.Ignore)]
public string BuyerBroker { get; set; }
[JsonProperty("Compensation to Buyer", NullValueHandling = NullValueHandling.Ignore)]
public long? CompensationToBuyer { get; set; }
}
using the code
var obj = JsonConvert.DeserializeObject<Transaction>(json);
However that gives me a Transaction.TransactionInformation object with 5 records each with all 5 elements with each record having all null values except for one of the 5 elements.
Is there a simple way to to return all 5 elements in one record?
Is there a simple way to to return all 5 elements in one record?
Sure -- Just put each property in a single record:
var finalRecord = new TransactionInformation
{
Type = obj.TransactionInformation.FirstOrDefault(x => !string.IsNullOrEmpty(x.Type))?.Type,
Action = obj.TransactionInformation.FirstOrDefault(x => !string.IsNullOrEmpty(x.Action))?.Action,
Owner = obj.TransactionInformation.FirstOrDefault(x => !string.IsNullOrEmpty(x.Owner))?.Owner,
BuyerBroker = obj.TransactionInformation.FirstOrDefault(x => !string.IsNullOrEmpty(x.BuyerBroker))?.BuyerBroker,
CompensationToBuyer = obj.TransactionInformation.FirstOrDefault(x => x.CompensationToBuyer.HasValue)?.CompensationToBuyer
};
That JSON data you are working with isn't in the most convenient format. In a perfect world it would look like this:
{
"Transaction Information": [{
"Type": "This is the Type",
"Action": "No",
"Owner": "Simpsons",
"Buyer/Broker": "Y",
"Compensation to Buyer": 3.0
}
]
}
Then what you were doing would have worked fine and you wouldn't have to do this last step to normalize the data.
You can create a custom JsonConverter that enables you to hook into the deserialization for the type:
public class TransactionConverter : JsonConverter<Transaction>
{
public override void WriteJson(JsonWriter writer, Transaction value, JsonSerializer serializer)
{
}
public override Transaction ReadJson(JsonReader reader, Type objectType, Transaction existingValue,
bool hasExistingValue, JsonSerializer serializer)
{
var rootChildren = JToken.ReadFrom(reader).First.Children();
var item = new TransactionInformation();
foreach (var child in rootChildren.Children())
{
item.Type ??= (string)child["Type"];
item.Action ??= (string) child["Action"];
item.BuyerBroker ??= (string)child["Buyer/Broker"];
item.Owner ??= (string)child["Owner"];
item.CompensationToBuyer ??= (long?)child["Compensation to Buyer"];
}
return new Transaction {TransactionInformation = new[] {item}};
}
public override bool CanRead => true;
}
and then call it:
var tx = JsonConvert.DeserializeObject<Transaction>(str, new TransactionConverter());

How to get deserialize object when type discriminator element on root json object using System.Text.Json

Sorry for my poor English.
I had success run code from this doc. I got new JSON data and there is another problem.
The JSON data is defined like this:
{
"id": 3,
"title": "aaa",
"typeDiscriminator": "search",
"settingDataTemp": {
"id": 11,
"type": "11",
"value": "11"
}
}
And the POCO class is defined as:
public class RootContent
{
public int id { get; set; }
public string title { get; set; }
public PageModuleType typeDiscriminator { get; set; }
public IPageSubContentSetting settingDataTemp { get; set; }
}
I want to deserialize settingDataTemp object, so the JsonConverter Read method like
public override IPageSubContentSetting Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
PageModuleType typeDiscriminator;
string rawText;
var discriminatorPropName = nameof(RootContent.typeDiscriminator);
using (var jsonDocument = JsonDocument.ParseValue(ref reader))
{
if (!jsonDocument.RootElement.TryGetProperty(discriminatorPropName, out var typeProperty))
{
throw new JsonException();
}
var result = Enum.TryParse(typeProperty.GetString(), true, out typeDiscriminator);
if (!result)
{
throw new JsonException();
}
rawText = jsonDocument.RootElement.GetRawText();
}
}
Can't get typeDiscriminator from JsonDocument.RootElement because it's defined on outer object.
Is there any way to get typeDiscriminator value?
I solved problem just use NewtonSoft.Json To edit JSON struct and copy typeDiscriminator value from root to settingDataTemp element.

How to deserialize JSON with dynamic and static key names in C#

I have to deserialize a response from an api which has the following structure:
[
{
"starttime": "...",
"endtime": "....",
"var1": {},
"var2": {}
},
{
"starttime": "...",
"endtime": "....",
"var1": {},
"var3": {}
},
{
"starttime": "...",
"endtime": "....",
"var1": {}
}
]
Some insights:
The JSON is an array of objects
Every object inside the array will ALWAYS have the properties "starttime" and "endtime"
Objects "var1", "var2", "var3" will ALWAYS have the same properties inside them... but the problem is that the object keys (var1, var2 or var3) are dynamic. It can be any string, and also the amount of this kind of objects is dynamic (I could have 3, or zero "var" objects).
I was thinking something like this, to deserialize the JSON string into a List of objects with properties "starttime", "endtime" and a dictionary with all the "var" objects.
public class MyResponse
{
[JsonProperty(PropertyName = "starttime")]
public string StartTime { get; set; }
[JsonProperty(PropertyName = "endtime")]
public string EndTime { get; set; }
public Dictionary<string, VarObject> VarData { get; set; }
}
But the VarData property is always null.
Has anyone tried something like this?
You have two options, the first is to deserialise directly to a List<Dictionary<string, object>>, for example:
var responses = JsonConvert.DeserializeObject<List<Dictionary<string, object>>>(json);
Alternatively, if you are stuck on using your object, you will need to write a custom converter. For example, something like this:
public class MyResponseConverter : JsonConverter
{
public override bool CanConvert(Type type) => type == typeof(MyResponse);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var responseObject = JObject.Load(reader);
MyResponse response = new MyResponse
{
StartTime = (string)responseObject["starttime"],
EndTime = (string)responseObject["endtime"],
};
var varData = new Dictionary<string, object>();
foreach (var property in responseObject.Properties())
{
if(property.Name == "starttime" || property.Name == "endtime")
{
continue;
}
varData.Add(property.Name, property.Value);
}
response.VarData = varData;
return response;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// If you want to write to JSON, you will need to implement this method
throw new NotImplementedException();
}
}
And your class would change slightly to this:
[JsonConverter(typeof(MyResponseConverter))]
public class MyResponse
{
[JsonProperty(PropertyName = "starttime")]
public string StartTime { get; set; }
[JsonProperty(PropertyName = "endtime")]
public string EndTime { get; set; }
public Dictionary<string, object> VarData { get; set; }
}
Now you deserialise like this:
var responses = JsonConvert.DeserializeObject<List<MyResponse>>(json);
You can view example Here https://dotnetfiddle.net/QgXWQi.
But for more flexibility, this logic is better to implement in method that marked as [OnDeserialized]
Like here https://www.newtonsoft.com/json/help/html/SerializationAttributes.htm
The main idea is to parse it as JObject and then convert to Dictionary
var jObj = JObject.Parse(jsonText);
var varData = jObj
.Children<JProperty>()
.Where(p => p.Name != "starttime" && p.Name != "endtime")
.ToDictionary(x=> x.Name, x => ((JObject)x.Value).ToObject<VarObject>());

Print object with nested properties as string of comma separated key-value pairs

I have a Person class:
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
}
public class Address
{
public string Line1 { get; set; }
public string Line2 { get; set; }
}
As an end result, I would like to print out a Person instance as JSON, but I'd like it to be a huge string of key-value pairs, e.g.:
"Name:John,Surname:Doe,Line1:Infinite Loop,Line2:California"
Notice that in the example above, I got rid of the actual class names (i.e. it prints out Line1 instead of Address.Line1) - i only care about all the name/value pairs.
So the end result would be an array of Persons:
"persons":[
"Name:John,Surname:Doe,Line1:Infinite Loop 1,Line2:California",
"Name:Jane,Surname:Doe,Line1:Infinite Loop 2,Line2:California"
]
As a starting point, I tried using reflection:
void Main()
{
var persons = new List<Person>();
persons.Add(new Person
{
Name = "John",
Surname = "Doe",
Address = new Address
{
Line1 = "Infinite Loop",
Line2 = "California"
}
});
foreach(var person in persons)
{
var properties = new List<string>();
foreach(var property in person.GetType().GetProperties())
{
properties.Add($"{property.Name}:{property.GetValue(person, null)}");
}
Console.WriteLine(string.Join(",", properties));
}
}
But I get the following output in LINQPad:
Name:John,Surname:Doe,Address:UserQuery+Address
I assume Address is not properly iterated upon because it's a nested object within Person. Even so, this doesn't look like the most clean/efficient approach.
You could do this using a custom JsonConverter like so:
class PersonConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Person);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var pairs = JObject.FromObject(value)
.Descendants()
.OfType<JProperty>()
.Where(p => p.Value is JValue)
.Select(p => p.Name + ":" + p.Value);
writer.WriteValue(string.Join(",", pairs));
}
public override bool CanRead
{
get { return false; }
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then use it by passing the converter to JsonConvert.SerializeObject like this:
string json = JsonConvert.SerializeObject(obj, Formatting.Indented, new PersonConverter());
Here is a working demo: https://dotnetfiddle.net/L4YDsm
Add a ToString override to your class and return a collection of strings as JSON.
public class Person
{
public string Name { get; set; }
public string Surname { get; set; }
public Address Address { get; set; }
public override string ToString()
{
return $"Name:{Name},Surname:{Surname},Line1:{Address?.Line1},Line2:{Address?.Line2}";
}
}
You must implement the reflection in a way that it could go deeper into the structure of your object in order to do what you want. Here's a simple recursive and generic adaptation to your provided code.
public static string GetRecursivePropertyValues(object obj)
{
var properties = new List<string>();
foreach (var property in obj.GetType().GetProperties())
{
object currentPropertyValue = property.GetValue(obj);
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
properties.Add($"{property.Name}:{currentPropertyValue}");
else
{
var subProperties = GetRecursivePropertyValues(currentPropertyValue);
properties.Add(subProperties);
}
}
return string.Join(";", properties);
}
This implementation first verifies if each property is defined with a primitive type or string (which are not considered primitive types in C#) and prints them normally if that's the case. Else, if the type is complex (like if it is declared as an Address instance), it recursivelly retrieves the properties of the complex object, and adds them to the resulting string.
You can call it like:
string propertiesStr = GetRecursivePropertyValues(person);
Edit: code now only flattens the object as required by OP (previous code sample used some rustic JSON formatting).
Using Newtonsoft.json
https://www.newtonsoft.com/json/help/html/SerializingJSON.htm
Check the above link... It will tell you how to serialize an object to json, it's important to note, you will want to add all your items to a list, then serialize the list into json to get the desired effect
Product product = new Product();
product.Name = "Apple";
product.ExpiryDate = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
There example:
string output = JsonConvert.SerializeObject(product);
//{
// "Name": "Apple",
// "ExpiryDate": "2008-12-28T00:00:00",
// "Price": 3.99,
// "Sizes": [
// "Small",
// "Medium",
// "Large"
// ]
//}

Consuming JSON elements in C# that are not in an array

I am currently consuming JSON output from one source that contains an array and one that does not.
The one with an array is simple, as I can create a class that represents the object and the list of objects, then iterate through the list and get the properties for each object. In the source that does not have an array, however, it is throwing me for a loop.
I do not know how to iterate through this. It seems as if I would need to create separate classes for "abc" and "def" even though the properties of each class are the same. Is there a simple way to do this?
Example that does not contain an array:
{
"objectContainer": {
"count": 25,
"objects": {
"abc": {
"name": "object1",
"parent": "0",
"status": "0",
},
"def": {
"name": "object2",
"parent": "0",
"status": "0",
}
etc....
Thanks in advance for any assistance.
You could use inheritance to prevent repeating the properties for "abc" and "def" over and over again.
public class Base
{
public string name { get; set; }
public string parent { get; set; }
public string status { get; set; }
}
public class Abc : Base { }
public class Def : Base { }
public class Objects
{
public Abc abc { get; set; }
public Def def { get; set; }
}
public class ObjectContainer
{
public int count { get; set; }
public Objects objects { get; set; }
}
public class RootObject
{
public ObjectContainer objectContainer { get; set; }
}
Then using JSON.NET you can deserialize the string.
var root = JsonConvert.DeserializeObject<RootObject>( json );
The problem is you're going to have to change the code every time you get a new object in there (e.g. ghi).
Another option, particularly if you're going to have different object names showing up, would be to just parse the JSON serially yourself.
JsonTextReader reader = new JsonTextReader( new StringReader( json ) );
while( reader.Read() )
{
if( reader.Value != null )
{
Console.WriteLine( "Field: {0}, Value: {1}", reader.TokenType, reader.Value );
}
}
Obviously where it's writing output to the Console you'd have to examine the TokenType and Value and stuff those into an object.
Update
This is pretty ugly, but I was curious how I might parse this into the object structure. You'd need to change the receiving object definitions a bit.
public class Base
{
public string name { get; set; }
public string parent { get; set; }
public string status { get; set; }
}
public class Objects
{
public List<Base> bases { get; set; }
public Objects()
{
bases = new List<Base>();
}
}
public class ObjectContainer
{
public int count { get; set; }
public Objects objects { get; set; }
public ObjectContainer()
{
objects = new Objects();
}
}
public class RootObject
{
public ObjectContainer objectContainer { get; set; }
public RootObject()
{
objectContainer = new ObjectContainer();
}
}
Then you can parse into it using:
while( reader.Read() )
{
if( reader.Value != null )
{
switch( reader.Depth )
{
case 2:
if( reader.TokenType == JsonToken.PropertyName && reader.Value.ToString() == "count" )
{
reader.Read();
root.objectContainer.count = Convert.ToInt32( reader.Value );
}
break;
case 3:
newBase = new Base();
root.objectContainer.objects.bases.Add( newBase );
break;
case 4:
if( reader.TokenType == JsonToken.PropertyName && reader.Value.ToString() == "name" )
{
reader.Read();
newBase.name = reader.Value.ToString();
}
if( reader.TokenType == JsonToken.PropertyName && reader.Value.ToString() == "parent" )
{
reader.Read();
newBase.parent = reader.Value.ToString();
}
if( reader.TokenType == JsonToken.PropertyName && reader.Value.ToString() == "status" )
{
reader.Read();
newBase.status = reader.Value.ToString();
}
break;
}
}
}
Not the prettiest code in the world but as long as the structure of the JSON doesn't change you'll end up with a nice object model no matter how many child objects or what their names are.
Based on your JSON above, you would probably need to create classes for those objects. You can abstract some of it away with inheritance as well. If possible, it would make more sense for "objects" to be an array that way you don't need to create separate objects. The name, and implementation both suggest an array.
If you cannot change the structure of the JSON, have a look at the bottom of the page at http://json.org/ for different libraries. Some may be more helpful than others. Json.NET is the one I usually use and you may have better results using something like their JSonReader so you don't have to create an overly complex object structure.
You could use the excellent (and dynamic) JObject class from the JSON.NET library like this:
// example input
var json = #"{""objectContainer"": {
""count"": 25,
""objects"": {
""abc"": {
""name"": ""object1"",
""parent"": ""0"",
""status"": ""0"",
},
""def"": {
""name"": ""object2"",
""parent"": ""0"",
""status"": ""0"",
}
}}";
var obj = JsonConvert.DeserializeObject(json);
var objectContainer = ((JObject)obj)["objectContainer"];
var abc = objectContainer["objects"]["abc"];
Console.WriteLine(abc["name"]);
The output is:
output1
You could even use directly the JObject.Parse() method to load and parse just a JSON code portion (for example if you can extract only the abc part from the complete JSON string):
var abc = JObject.Parse(#"{""abc"": {
""name"": ""object1"",
""parent"": ""0"",
""status"": ""0"",
}}")["abc"];
Console.WriteLine(abc["name"]);
var abcd = JObject.Parse(#"{
""name"": ""object1"",
""parent"": ""0"",
""status"": ""0"",
}");
Console.WriteLine(abcd["name"]);
You could then assign the extracted values to your custom class.
Using the library and the JObject class you don't need to represent the JSON with a class. The downside is, that you don't have the type safety of the class and it's properties.
Update
You could iterate over the properties / objects without knowing their names:
var obj = JsonConvert.DeserializeObject(json);
var objectContainer = ((JObject)obj)["objectContainer"];
foreach (var o in objectContainer["objects"])
{
if (o is JProperty)
{
var op = ((JProperty)o);
Console.WriteLine("{0} - {1}", op.Name, op.Value);
}
}
The output is:
abc - {
"name": "object1",
"parent": "0",
"status": "0"
}
def - {
"name": "object2",
"parent": "0",
"status": "0"
}

Categories

Resources