Web API - JSON serialize properties as array of strings - json

I have a class
public class Person
{
public string Name { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
I'd like to serialize it into:
{ "Person": "[John, Smith, 13]" }
instead of
{"Name":"John", "LastName":"Smith", "Age": 13}
Is it possible to achieve this without writing a custom serializer? If not, how do I apply a custom serializer only to a specific class?

Since the json string you want to get when serializing an instance of Person is not the standard one, you'll need to write a custom converter and decorate your class with it. One way to do that is the following. Write class that inherits from JsonConverter:
public class CustomConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
if (value == null) return;
writer.WriteStartObject();
writer.WritePropertyName(value.GetType().Name);
writer.WriteStartArray();
var properties = value.GetType().GetProperties();
foreach (var property in properties)
writer.WriteValue(value.GetType().GetProperty(property.Name).GetValue(value));
writer.WriteEndArray();
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof (Person);
}
}
Decorate your class with the JsonConverter attribute:
[JsonConverter(typeof(CustomConverter))]
public class Person
{
public string Name { get; set; }
public string LastName { get; set; }
public int Age { get; set; }
}
Now lets say that you use the following code:
var person = new Person
{
Name = "John",
LastName = "Smith",
Age = 13
};
var json = JsonConvert.SerializeObject(person, Formatting.Indented);
The json varible wil have this value:
{
"Person": [
"John",
"Smith",
13
]
}
Demo here

Related

Deserializing Json With Unknown Key - ASP.Net Core 3.1

I'd like to deserialize the following JSON with .Net 3.1
My difficulty is the "1234" key for this object is unknown when this object is serialized. How could I deserialize this? The values I want to keep are the nested "first_name" and "last_name" attributes
{
"1234":{
"id":1234,
"first_name":"John",
"last_name":"Doe"
}
}
Any help is appreciated!
In case you're using the Newtonsoft.Json.JsonConverter library, you could create a custom JsonConverter to handle dynamic properties and override the ReadJson and WriteJson methods.
public class MyConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
// write your custom read json
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
// write your custom write json
}
}
https://www.newtonsoft.com/json/help/html/T_Newtonsoft_Json_JsonConverter.htm
You could use a dictionary:
public class Child
{
[JsonPropertyName("id")]
public string ID { get; set; }
[JsonPropertyName("first_name")]
public string FirstName { get; set; }
[JsonPropertyName("last_name")]
public string LastName { get; set; }
}
public class Parent
{
public Dictionary<string, Child> Children { get; set; }
}
You can then enumerate through Children using Foreach or Linq.

.NET NewtonSoft JSON deserialize with different property name [duplicate]

how can I deserialize below json structure using newtonsoft json.net in .net.
{
"users" : {
"parentname":"test",
"100034" : {
"name" : "tom",
"state" : "WA",
"id" : "cedf-c56f-18a4-4b1"
},
"10045" : {
"name" : "steve",
"state" : "NY",
"id" : "ebb2-92bf-3062-7774"
},
"12345" : {
"name" : "mike",
"state" : "MA",
"id" : "fb60-b34f-6dc8-aaf7"
}
}
}
I tried below code but its not working. I got error 'Error converting value "test" to type 'ConsoleApplication2.User'. Path 'users.parentname', line 5, position 35.'
class Program
{
static void Main(string[] args)
{
string json = #"
{
""users"": {
""parentname"":""test"",
""10045"": {
""name"": ""steve"",
""state"": ""NY"",
""id"": ""ebb2-92bf-3062-7774""
}
}
}";
RootObject root = JsonConvert.DeserializeObject<RootObject>(json);
}
}
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Please suggest.
You have a couple problems:
Your JSON has an extra level of nesting, with the root object containing a single property "users":
{
"users" : { ... }
}
Your data model needs to reflect this.
Your "users" object has a mixture of known and unknown property names. The question Deserialize json with known and unknown fields addresses a similar situation, however in your case your unknown properties always have a fixed schema and their values should be deserialized into a dictionary of POCOs -- specifically the User class. Therefore the answers there don't quite meet your needs, nor does the build-in functionality [JsonExtensionData].
The following converter allows for unknown properties to be deserialized into a typed container, rather than into an dictionary of arbitrary types:
[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)]
public class JsonTypedExtensionDataAttribute : Attribute
{
}
public class TypedExtensionDataConverter<TObject> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(TObject).IsAssignableFrom(objectType);
}
JsonProperty GetExtensionJsonProperty(JsonObjectContract contract)
{
try
{
return contract.Properties.Where(p => p.AttributeProvider.GetAttributes(typeof(JsonTypedExtensionDataAttribute), false).Any()).Single();
}
catch (InvalidOperationException ex)
{
throw new JsonSerializationException(string.Format("Exactly one property with JsonTypedExtensionDataAttribute is required for type {0}", contract.UnderlyingType), ex);
}
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var jObj = JObject.Load(reader);
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(objectType);
var extensionJsonProperty = GetExtensionJsonProperty(contract);
var extensionJProperty = (JProperty)null;
for (int i = jObj.Count - 1; i >= 0; i--)
{
var property = (JProperty)jObj.AsList()[i];
if (contract.Properties.GetClosestMatchProperty(property.Name) == null)
{
if (extensionJProperty == null)
{
extensionJProperty = new JProperty(extensionJsonProperty.PropertyName, new JObject());
jObj.Add(extensionJProperty);
}
((JObject)extensionJProperty.Value).Add(property.RemoveFromLowestPossibleParent());
}
}
var value = existingValue ?? contract.DefaultCreator();
using (var subReader = jObj.CreateReader())
serializer.Populate(subReader, value);
return value;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var contract = (JsonObjectContract)serializer.ContractResolver.ResolveContract(value.GetType());
var extensionJsonProperty = GetExtensionJsonProperty(contract);
JObject jObj;
using (new PushValue<bool>(true, () => Disabled, (canWrite) => Disabled = canWrite))
{
jObj = JObject.FromObject(value, serializer);
}
var extensionValue = (jObj[extensionJsonProperty.PropertyName] as JObject).RemoveFromLowestPossibleParent();
if (extensionValue != null)
{
for (int i = extensionValue.Count - 1; i >= 0; i--)
{
var property = (JProperty)extensionValue.AsList()[i];
jObj.Add(property.RemoveFromLowestPossibleParent());
}
}
jObj.WriteTo(writer);
}
[ThreadStatic]
static bool disabled;
// Disables the converter in a thread-safe manner.
bool Disabled { get { return disabled; } set { disabled = value; } }
public override bool CanWrite { get { return !Disabled; } }
public override bool CanRead { get { return !Disabled; } }
}
public struct PushValue<T> : IDisposable
{
Action<T> setValue;
T oldValue;
public PushValue(T value, Func<T> getValue, Action<T> setValue)
{
if (getValue == null || setValue == null)
throw new ArgumentNullException();
this.setValue = setValue;
this.oldValue = getValue();
setValue(value);
}
#region IDisposable Members
// By using a disposable struct we avoid the overhead of allocating and freeing an instance of a finalizable class.
public void Dispose()
{
if (setValue != null)
setValue(oldValue);
}
#endregion
}
public static class JsonExtensions
{
public static TJToken RemoveFromLowestPossibleParent<TJToken>(this TJToken node) where TJToken : JToken
{
if (node == null)
return null;
var contained = node.AncestorsAndSelf().Where(t => t.Parent is JContainer && t.Parent.Type != JTokenType.Property).FirstOrDefault();
if (contained != null)
contained.Remove();
// Also detach the node from its immediate containing property -- Remove() does not do this even though it seems like it should
if (node.Parent is JProperty)
((JProperty)node.Parent).Value = null;
return node;
}
public static IList<JToken> AsList(this IList<JToken> container) { return container; }
}
Then use it in your classes as follows:
class RootObject
{
[JsonProperty("users")]
public Users Users { get; set; }
}
[JsonConverter(typeof(TypedExtensionDataConverter<Users>))]
class Users
{
public Users()
{
this.UserTable = new Dictionary<string, User>();
}
[JsonProperty("parentname")]
public string ParentName { get; set; }
[JsonTypedExtensionData]
public Dictionary<string, User> UserTable { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
}
I wrote the converter in a fairly general way so it can be reused. A converter that is hardcoded for the Users type would require less code.
Your Json has to look like this:
{
"ParentName":"test",
"users":{
"10045":{
"name":"steve",
"state":"NY",
"id":"ebb2-92bf-3062-7774",
"ParentName":"someOtherName"
}
}
}
In order to deserialize it with your given class structure:
class RootObject
{
public string ParentName { get; set; }
public Dictionary<string, User> users { get; set; }
}
class User
{
public string name { get; set; }
public string state { get; set; }
public string id { get; set; }
public string ParentName { get; set; }
}
Now you can deserialize the Json string with:
var root = JsonConvert.DeserializeObject<RootObject>(json);

Newtonsoft.Json Parsing an Object with Changing Type [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 2 years ago.
I'm working on writing a .Net Standard library for working with the UptimeRobot API. I've found an endpoint that sometimes returns an array of int and sometimes returns a single 0.
The field I'm working with is monitors.
{
"stat": "ok",
"pagination": {
"offset": 0,
"limit": 50,
"total": 2
},
"psps": [
{
"id": 2345678,
"friendly_name": "Full Status",
"monitors": 0,
"sort": 1,
"status": 1,
"standard_url": "https://stats.uptimerobot.com/xxxxxx",
"custom_url": ""
},
{
"id": 123456,
"friendly_name": "Filtered Status",
"monitors": [
783210435,
783210481
],
"sort": 1,
"status": 1,
"standard_url": "https://stats.uptimerobot.com/xxxxxx",
"custom_url": ""
}
]
}
This is my model:
using Newtonsoft.Json;
using System.Collections.Generic;
using SharpenUp.Common.Types;
using System.Diagnostics.CodeAnalysis;
namespace SharpenUp.Common.Models.PublicStatusPages
{
public class PublicStatusPage
{
[ExcludeFromCodeCoverage]
[JsonProperty( PropertyName = "id" )]
public int Id { get; set; }
[JsonProperty( PropertyName = "friendly_name" )]
public string Name { get; set; }
[JsonProperty( PropertyName = "monitors" )]
public List<int> Monitors { get; set; }
[JsonProperty( PropertyName = "sort" )]
public int Sort { get; set; }
[JsonProperty( PropertyName = "status" )]
public PublicStatusPageStatusType Status { get; set; }
[JsonProperty( PropertyName = "standard_url" )]
public string StandardURL { get; set; }
[JsonProperty( PropertyName = "custom_url" )]
public string CustomURL { get; set; }
}
}
The Monitors object will parse if I limit my query to only the result with the array, but fails if I try to bring in the other.
Any ideas? I'm not even 100% certain what wording to use to try and figure this out.
You could use a Converter which converts cases where there is only a single item to List. For example, considering the following converter.
class SingleOrArrayConverter<T> : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(List<T>));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Array)
{
return token.ToObject<List<T>>();
}
return new List<T> { token.ToObject<T>() };
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
You could now decorate your Monitors property with JsonConverter attribute as
[JsonConverter(typeof(SingleOrArrayConverter<int>))]
[JsonProperty( PropertyName = "monitors" )]
public List<int> Monitors { get; set; }
This would allow you to deserialize your Json correctly.
Sample Output

Deserializing class with abstract property JSON

public abstract class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
[JsonProperty(Required = Required.Always)]
public string Type { get; set; }
}
public class Employee : Person
{
public string Department { get; set; }
public string JobTitle { get; set; }
}
public class Artist : Person
{
public string Skill { get; set; }
}
I already have a JSON converter working to deserialize such objects based on the value of the Type property.
public abstract class JsonCreationConverter<T> : JsonConverter
{
protected abstract T Create(Type objectType, JObject jObject);
public override bool CanConvert(Type objectType)
{
Type t = typeof(T);
return typeof(T).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
public class PersonConverter : JsonCreationConverter<Person>
{
protected override Person Create(Type objectType, JObject jObject)
{
if(jObject["Type"] == null)
{
throw new ArgumentException();
}
string type = (string)jObject["Type"];
if(type == null)
{
throw new ArgumentException();
}
if(type.Equals("Employee", StringComparison.InvariantCultureIgnoreCase))
{
return new Employee();
}
else if (type.Equals("Artist", StringComparison.InvariantCultureIgnoreCase))
{
return new Artist();
}
return null;
}
}
string json = "[{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]";
List<Person> person = JsonConvert.DeserializeObject<List<Person>>(json, new PersonConverter());
The above works well.
Now, I have the following class:
public class City
{
public string Name { get; set; }
public int Population { get; set; }
public Person[] Persons { get; set; }
}
How do I write a converter for this City class that can use the PersonConverter to initialize the Persons property? My initial thought was to extract the Persons part as JObject, and then call Deserialize on it using PersonConverter in its ReadJson method, similar to the sample below.
var p = jObject["Persons"].ToString();
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());
But ReadJson throws an exception in the Serializer.Populate method since abstract classes cannot be instantiated.
The following is the JSON string as an example:
string Cityjson = "{\"Name\": \"London\" , \"Population\": \"1000\" , \"Persons\": [{\"Department\": \"Department1\",\"JobTitle\": \"JobTitle1\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Employee\"},{\"Skill\": \"Drawer\",\"FirstName\": \"FirstName1\",\"LastName\": \"LastName1\",\"Type\": \"Artist\"}]}";
Approach #1
I solved this by
Marking the Persons property to be ignored in deserialization
[JsonIgnore]
public Person[] Persons { get; set; }
In the Create method, instantiating the City object and using PersonConverter to initialize the Persons property
protected override City Create(Type objectType, JObject jObject)
{
if (jObject["Persons"] == null)
{
throw new ArgumentException();
}
var p = jObject["Persons"].ToString();
List<Person> persons = JsonConvert.DeserializeObject<List<Person>>(p, new PersonConverter());
var city = new City();
city.Persons = persons.ToArray();
return city;
}
The ReadJson method would populate the remaining City properties as usual.
Are there any other approaches?
I think this is the most suitable way
In ReadJson when array is passed it was basically crashing since Jarray is not jboject. So, I updated the ReadJson as follows and it worked.
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.StartArray)
{
JArray jObject = JArray.Load(reader);
List<T> list = new List<T>();
for (int i = 0; i < jObject.Count(); i++)
{
var p = jObject[i];
JObject ob = p as JObject;
T value = Create(objectType, ob);
serializer.Populate(ob.CreateReader(), value);
list.Add(value);
}
return list.ToArray();
}
else
{
JObject jObject = JObject.Load(reader);
T target = Create(objectType, jObject);
serializer.Populate(jObject.CreateReader(), target);
return target;
}
}
And yes, I don't need a CityConverter. Adding PersonConverter is enough.

JSON object setting to default /null value [duplicate]

I'm using Json.NET for a project I'm working on.
From an external API, I am receiving JSON with properties that are objects, but when they are empty 'false' is passed.
For example:
data: {
supplier: {
id: 15,
name: 'TheOne'
}
}
Could also be:
data: {
supplier: false
}
How should I define the supplier property so that the supplier will be deserialized to a Supplier object or null.
Right now I have:
public class Data {
[JsonProperty("supplier")]
public SupplierData Supplier { get; set; }
}
public class SupplierData {
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
}
But now when trying to deserialize when supplier has a value of 'false' it fails.
I would like the Supplier property to be null when the JSON value is 'false'.
I hope someone knows how to do this. Thanks.
This can be solved by making a custom JsonConverter for your SupplierData class. Here is what the converter might look like:
class SupplierDataConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(SupplierData));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Object)
{
return token.ToObject<SupplierData>();
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, value);
}
}
To use it, all you would need to do is add a [JsonConverter] attribute to the Supplier property in your Data class like this:
public class Data
{
[JsonProperty("supplier")]
[JsonConverter(typeof(SupplierDataConverter))]
public SupplierData Supplier { get; set; }
}
Below is a demonstration of the converter in action. Note that the demo assumes you have some kind of containing object for the data property, since the JSON in your question can't stand on its own. I defined a class called RootObject for this purpose:
public class RootObject
{
[JsonProperty("data")]
public Data Data { get; set; }
}
The actual demo code follows:
class Program
{
static void Main(string[] args)
{
string json = #"
{
""data"":
{
""supplier"":
{
""id"": 15,
""name"": ""TheOne""
}
}
}";
Console.WriteLine("--- first run ---");
RootObject obj = JsonConvert.DeserializeObject<RootObject>(json);
DumpSupplier(obj.Data.Supplier);
json = #"
{
""data"":
{
""supplier"": false
}
}";
Console.WriteLine("--- second run ---");
obj = JsonConvert.DeserializeObject<RootObject>(json);
DumpSupplier(obj.Data.Supplier);
}
static void DumpSupplier(SupplierData supplier)
{
if (supplier != null)
{
Console.WriteLine("Id: " + supplier.Id);
Console.WriteLine("Name: " + supplier.Name);
}
else
{
Console.WriteLine("(null)");
}
Console.WriteLine();
}
}
And here is the output from the above:
--- first run ---
Id: 15
Name: TheOne
--- second run ---
(null)