deserialize json with different propertyValue types - c#

I want to deserialize (and afterwards serialize again) the following Json
{
"id": "12345",
"custom_fields": [
{
"definition": "field1",
"value": "stringvalue"
},
{
"definition": "field2",
"value": [ "arrayvalue1", "arrayvalue2" ]
},
{
"definition": "field3",
"value": {
"type": "user",
"id": "1245"
}
}
]
}
The type of the value if different (field1: string, field2: array, field3: an extra nested structure).
This what I have:
public class CustomField
{
public string Definition { get; set; }
public object Value { get; set; }
}
public class RootObject
{
public string Id { get; set; }
public List<CustomField> Custom_fields { get; set; }
}
The Value-field is defined as object, but this is a problem when serializing again, especially the nested structure for field3.
How can I solve this?

You could implement a custom converter for your Value property:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
namespace Test
{
public class Program
{
static void Main(string[] args)
{
var json =
#"{
""id"": ""12345"",
""custom_fields"": [
{
""definition"": ""field1"",
""value"": ""stringvalue""
}, {
""definition"": ""field2"",
""value"": [ ""arrayvalue1"", ""arrayvalue2"" ]
}, {
""definition"": ""field3"",
""value"": {
""type"": ""user"",
""id"": ""1245""
}
}
]
}";
var rootObject = JsonConvert.DeserializeObject<RootObject>(json);
Console.WriteLine(JsonConvert.SerializeObject(rootObject));
}
}
public class RootObject
{
[JsonProperty(PropertyName = "id")]
public string Id { get; set; }
[JsonProperty(PropertyName = "custom_fields")]
public List<CustomField> CustomFields { get; set; }
}
public class CustomField
{
[JsonProperty(PropertyName = "definition")]
public string Definition { get; set; }
[JsonProperty(PropertyName = "value")]
[JsonConverter(typeof(CustomValueConverter))]
public dynamic Value { get; set; }
}
public class CustomValueConverter : JsonConverter
{
// By returning false, we let the default `WriteJson` kick in
public override bool CanWrite => false;
public override bool CanConvert(Type objectType)
{
return true;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return string.Empty;
}
if (reader.TokenType == JsonToken.String)
{
return serializer.Deserialize<string>(reader);
}
if (reader.TokenType == JsonToken.StartArray)
{
return serializer.Deserialize<string[]>(reader);
}
return serializer.Deserialize<Dictionary<string, string>>(reader);
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
}

Related

How to deserialize JSON to two differnet classes according to object type?

I'm trying to read a JSON file with this format:
{
"A":
{
"type": "option_a",
"state": 0,
},
"B":
{
"type": "option_b",
"state": 1,
},
"C":
{
"type": "option_a",
"state": 0,
},
.
.
.
"Z":
{
"MODE A":
{
"status a": 0,
"status b": 0,
},
"MODE B":
{
"status a": 0,
"status b": 1,
},
"MODE C":
{
"status a": 1,
"status b": 0,
},
}
As I see it that the correspending classes should be:
public class ClassA
{
public string Type {get; set;}
public int State {get; set;}
}
// for handeling the 'Z' structure:
public class ClassB
{
public ClassBInternalStructure Mode {get; set;}
}
public class ClassBInternalStructure
{
public int StatusA {get; set;}
public int StatusB {get; set;}
I've searched a lot of simialer questions, none with the exact sloution for this case,
as far as I managed to find.
If I understand correctly the right approach to such a task is to used the Newtonsoft JsonConvert.
Here Is what I've broght up till now (C# 8), I would be grateful for any help to fill the missing content (hopefully i'm in the correct direction):
public class MyJsonConverter : JsonConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JToken token = JToken.Load(reader);
if (token.Type == JTokenType.Object)
{
//????
}
else if (token.Type == JTokenType.String)
{
//????
}
else
{
throw new NotSupportedException("...");
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
Another part which I didn't manage to figure is how to invoke this method -
public class JsonParser
{
public void Parsing(string filePath)
{
try
{
using StreamReader sr = new(filePath);
string JsonData = File.ReadAllText(filePath);
// is another 'MyJsonConverter' override class needed?
???.ReadJson(????, null, null, new Newtonsoft.Json.JsonSerializer());
}
catch (Exception)
{
throw;
}
}
}
Thanks in advnace!
correct classes
public partial class test
{
public A A { get; set; }
public A B { get; set; }
public A C { get; set; }
public Dictionary<string, Z> Z { get; set; }
}
public partial class A
{
public string Type { get; set; }
public string State { get; set; }
}
public partial class Z
{
public string StatusA { get; set; }
public string StatusB { get; set; }
}

How to avoid incompatible during deserializing polymorphic properties in MassTransit?

When using the request/response pattern, during the deserialization of the response, an incompatibility error occurs. Apparently, the publisher serializer configuration is causing this issue, although the message is as expected.
Using the JsonProperty feature could isolate the problem, however, it does not reflect what was expected.
Versions
.NET: 6.0;
MassTRansit: 7.3.0;
Newtonsoft: 13.0.1
Type specified in JSON 'Messages.Models+CreditCard, Messages'
is not compatible with 'GreenPipes.DynamicInternal.Models\+Messages.Models\+IPaymentMethod'
Resource:
_bus.CreateRequestClient<TMessage>().GetResponse<TResponse>(message, cancellationToken);
Publisher serializer configuration:
bus.ConfigureJsonSerializer(settings =>
{
settings.TypeNameHandling = TypeNameHandling.Objects;
return settings;
});
Message response serialization:
{
"message": {
"$type": "Messages.Services.ShoppingCarts.Responses+CartDetails, Messages",
"cartItems": [
{
"$type": "Messages.Models+Item, Messages",
"productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"productName": "string",
"unitPrice": "10.0",
"quantity": 30,
"pictureUrl": "string"
}
],
"paymentMethods": [
{
"$type": "Messages.Models+CreditCard, Messages",
"id": "be7c40ac-1cd1-4e35-bccf-a1a2f4efecfd",
"expiration": "01/22",
"holderName": "string",
"number": "374245455400126",
"securityNumber": "string"
},
{
"$type": "Messages.Models+PayPal, Messages",
"id": "9465cf12-a322-477d-94c8-116d03a8399e",
"Password": "123",
"UserName": "string"
}
],
...
}
}
Consumer deserializer configuration:
bus.ConfigureJsonDeserializer(settings =>
{
settings.TypeNameHandling = TypeNameHandling.Objects; // or Auto, All, etc...
return settings;
});
Error:
System.Runtime.Serialization.SerializationException: A JSON serialization exception occurred while deserializing the message envelope
---> Newtonsoft.Json.JsonSerializationException: Type specified in JSON 'Messages.Models+CreditCard, Messages, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null'
is not compatible with 'GreenPipes.DynamicInternal.Models\+Messages.Models\+IPaymentMethod, MessagesGreenPipes.DynamicInternale7ccc67139ad479db488c4fa6310335a, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
Path 'message.paymentMethods[0].$type', line 30, position 55.
JsonProperty does not work:
public record CartDetails : Response
{
[JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public IEnumerable<Models.IPaymentMethod> PaymentMethods { get; init; }
}
{
"message": {
"cartItems": [
{
"productId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
"productName": "string",
"unitPrice": "10.0",
"quantity": 30,
"pictureUrl": "string"
}
],
"paymentMethods": [
{
"id": "be7c40ac-1cd1-4e35-bccf-a1a2f4efecfd",
"expiration": "01/22",
"holderName": "string",
"number": "374245455400126",
"securityNumber": "string"
},
{
"id": "9465cf12-a322-477d-94c8-116d03a8399e",
"Password": "123",
"UserName": "string"
}
],
...
}
}
Response
public record CartDetails : Response
{
// [JsonProperty(TypeNameHandling = TypeNameHandling.Objects)]
public IEnumerable<Models.IPaymentMethod> PaymentMethods { get; init; }
public IEnumerable<Models.Item> CartItems { get; init; }
public Guid UserId { get; init; }
public decimal Total { get; init; }
public Guid Id { get; init; }
public bool IsDeleted { get; init; }
}
Types:
public interface IPaymentMethod
{
Guid Id { get; }
decimal Amount { get; }
}
public record CreditCard : IPaymentMethod
{
public Guid Id { get; init; }
public decimal Amount { get; init; }
[property: JsonConverter(typeof(ExpirationDateOnlyJsonConverter))]
public DateOnly Expiration { get; init; }
public string HolderName { get; init; }
public string Number { get; init; }
public string SecurityNumber { get; init; }
}
public record PayPal : IPaymentMethod
{
public Guid Id { get; init; }
public decimal Amount { get; init; }
public string UserName { get; init; }
public string Password { get; init; }
}
Thanks Wiktor Zychla, the issue led me to Nkot's workaround, which served me very well:
TypeNameHandlingConverter:
internal class TypeNameHandlingConverter : JsonConverter
{
private readonly TypeNameHandling _typeNameHandling;
public TypeNameHandlingConverter(TypeNameHandling typeNameHandling)
{
_typeNameHandling = typeNameHandling;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
=> new JsonSerializer { TypeNameHandling = _typeNameHandling }.Serialize(writer, value);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
=> new JsonSerializer { TypeNameHandling = _typeNameHandling }.Deserialize(reader, objectType);
public override bool CanConvert(Type objectType)
=> IsMassTransitOrSystemType(objectType) is false;
private static bool IsMassTransitOrSystemType(Type objectType)
=> objectType.Assembly == typeof(IConsumer).Assembly ||
objectType.Assembly.IsDynamic ||
objectType.Assembly == typeof(object).Assembly;
}
Consumer deserializer configuration:
bus.ConfigureJsonDeserializer(settings =>
{
settings.Converters.Add(new TypeNameHandlingConverter(TypeNameHandling.Objects));
return settings;
});

How to write a WebService that will accept Json with "Multi Type Property"?

Context:
Cust has a service that send the following json. He can easly change the target of that query but not the query it self.
I have to build a WebService that accept query like the following JSON.
While I will have no issue handeling the Json, I have an issue trying to define the method/interface that will accept a query like this.
The issue comes from Houses > Things: It's a dictionary of string, "objectThing" where "objectThing" has a property value that may hold multiple type.
EG:
int , "Value": 42
string , "Value": "Catty"
string array, "Value": ["Book1", "Book2", "Book3"]
object , A limited List of Object Type
"Value":
{
"PeopleId": "1234ABCD",
"Name": "John"
}
object array, An Array of with limited List of Object Type
"Value": [
{
"PeopleId": "1234ABCD",
"Name": "John"
},
{
"PeopleId": "0000AAAA",
"Name": "Doe"
}
]
Value is not Dynamic for me. It's within a limited list of Type that I can define.
Json Example:
{
"RootID" : "0123456",
"FooID" : "0123456",
"BarID" : "0123456",
"Houses" :[
{
"OwnerId" : "0123456",
"Date" : 1890895600000,
"Location" : {
"Latitude" : -1,
"Longitude" : -1
},
"Things" :{
"1" :{
"Label": "Books",
"Type" : "List",
"Value": ["Book1", "Book2", "Book3"]
},
"2" :{
"Label": "Cat",
"Type" : "Text",
"Value": "Catty"
},
"3" :{
"Label": "A Number",
"Type" : "Int",
"Value": 42
},
"4" :{
"Label": "Peoples",
"Type" : "People",
"Value": [
{
"PeopleId": "1234ABCD",
"Name": "John"
},
{
"PeopleId": "0000AAAA",
"Name": "Doe"
}
]
}
}
},
{
"OwnerId" : "111111",
"Things" :{}
},
{
"OwnerId" : "000001",
"Things" :{}
}
]
}
And the Class definition, If I were to deserialize this Json into a proper type:
using System;
using System.Collections.Generic;
using System.Globalization;
using Newtonsoft.Json;
using Newtonsoft.Json.Converters;
public partial class QueryRoot
{
[JsonProperty("RootID")]
public string RootId { get; set; }
[JsonProperty("FooID")]
public string FooId { get; set; }
[JsonProperty("BarID")]
public string BarId { get; set; }
[JsonProperty("Houses")]
public List<House> Houses { get; set; }
}
public partial class House
{
[JsonProperty("OwnerId")]
public string OwnerId { get; set; }
[JsonProperty("Date", NullValueHandling = NullValueHandling.Ignore)]
public long? Date { get; set; }
[JsonProperty("Location", NullValueHandling = NullValueHandling.Ignore)]
public Location Location { get; set; }
[JsonProperty("Things")]
public Dictionary<string, Thing> Things { get; set; }
}
public partial class Location
{
[JsonProperty("Latitude")]
public long Latitude { get; set; }
[JsonProperty("Longitude")]
public long Longitude { get; set; }
}
public partial class Thing
{
[JsonProperty("Label")]
public string Label { get; set; }
[JsonProperty("Type")]
public string Type { get; set; }
[JsonProperty("Value")]
public ThingValue Value { get; set; }
}
public partial class ValueClass
{
[JsonProperty("PeopleId")]
public string PeopleId { get; set; }
[JsonProperty("Name")]
public string Name { get; set; }
}
public partial struct ValueElement
{
public string String;
public ValueClass ValueClass;
public static implicit operator ValueElement(string String) => new ValueElement { String = String };
public static implicit operator ValueElement(ValueClass ValueClass) => new ValueElement { ValueClass = ValueClass };
}
public partial struct ThingValue
{
public List<ValueElement> AnythingArray;
public long? Integer;
public string String;
public static implicit operator ThingValue(List<ValueElement> AnythingArray) => new ThingValue { AnythingArray = AnythingArray };
public static implicit operator ThingValue(long Integer) => new ThingValue { Integer = Integer };
public static implicit operator ThingValue(string String) => new ThingValue { String = String };
}
public partial class QueryRoot
{
public static QueryRoot FromJson(string json) => JsonConvert.DeserializeObject<QueryRoot>(json, QuickType.Converter.Settings);
}
public static class Serialize
{
public static string ToJson(this QueryRoot self) => JsonConvert.SerializeObject(self, QuickType.Converter.Settings);
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
ThingValueConverter.Singleton,
ValueElementConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class ThingValueConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(ThingValue) || t == typeof(ThingValue?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.Integer:
var integerValue = serializer.Deserialize<long>(reader);
return new ThingValue { Integer = integerValue };
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new ThingValue { String = stringValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<List<ValueElement>>(reader);
return new ThingValue { AnythingArray = arrayValue };
}
throw new Exception("Cannot unmarshal type ThingValue");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (ThingValue)untypedValue;
if (value.Integer != null)
{
serializer.Serialize(writer, value.Integer.Value);
return;
}
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
if (value.AnythingArray != null)
{
serializer.Serialize(writer, value.AnythingArray);
return;
}
throw new Exception("Cannot marshal type ThingValue");
}
public static readonly ThingValueConverter Singleton = new ThingValueConverter();
}
internal class ValueElementConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(ValueElement) || t == typeof(ValueElement?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new ValueElement { String = stringValue };
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<ValueClass>(reader);
return new ValueElement { ValueClass = objectValue };
}
throw new Exception("Cannot unmarshal type ValueElement");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (ValueElement)untypedValue;
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
if (value.ValueClass != null)
{
serializer.Serialize(writer, value.ValueClass);
return;
}
throw new Exception("Cannot marshal type ValueElement");
}
public static readonly ValueElementConverter Singleton = new ValueElementConverter();
}
I already have a WCF Service that handles Json. It work fine the issue is declaring the method/interface that will accept this kind of query.
If WCF Web Service is a limiting factor, or if (ASP.NET/Core) Web APIs provides an easier path it's welcom.
You can receive a JSON string and convert it to an object. Here is a demo:
[WebMethod]
public string HelloWorld()
{
Stream s = HttpContext.Current.Request.InputStream;
byte[] b = new byte[s.Length];
s.Read(b, 0, (int)s.Length);
string jsontext = Encoding.UTF8.GetString(b);
var productProperty = JsonHelper.JsonDeserialize<School>(jsontext); //Deserialize JSON strings to objects
return "Hello World";
}
This is the method in WebService.
[DataContract]
public class School
{
[DataMember]
public int ClassroomId { set; get; }
[DataMember]
public List<Student> StudentList { set; get; }
}
[DataContract]
public class Student
{
[DataMember]
public int StudentId { set; get; }
[DataMember]
public string StudentName { set; get; }
}
This is the object to be converted by JSON string.
public class JsonHelper
{
public static string JsonSerializer<T>(T t)
{
var ser = new DataContractJsonSerializer(typeof(T));
var ms = new MemoryStream();
ser.WriteObject(ms, t);
string jsonString = Encoding.UTF8.GetString(ms.ToArray());
ms.Close();
return jsonString;
}
public static T JsonDeserialize<T>(string jsonString)
{
var ser = new DataContractJsonSerializer(typeof(T));
var ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
var obj = (T)ser.ReadObject(ms);
return obj;
}
}
To deserialize JSON characters into objects,there are many open-source class libraries. I use the DatacontractJsonSerializer that comes with .net version 3.5 or above.I wrote a JsonHelper class.

Create a class to build a JSON structure where one of its variables is again has all the variables from its own class (C#)

I've been pondering about this for some time and I think I might just be missing an essential basic coding approach to resolve.
This is what I tried:
class DataSetCommonQuery
{
public string #operator;
public List<DataSetCommonQuery> rules = new List<DataSetCommonQuery>();
}
My expected JSON should be:
{
"payLoad": {
"DataSetCommonQuery": {
"operator": "AND",
"rules": [{
"field": "ENTITY.CIFNumber",
"condition": "<>",
"value": "3123"
},
{
"field": "ENTITY.Country",
"condition": "LIKE",
"value": "USA"
},
{
"operator": "OR",
"rules": [{
"field": "ENTITY.FYEMonth",
"condition": "=",
"value": "May"
},
{
"field": "STATEMENT.ProfitBeforeTax",
"condition": ">=",
"value": 123123
},
{
"field": "STATEMENT.NetSales",
"condition": "<=",
"value": 234234
},
{
"field": "STATEMENT.statementdatekey_",
"condition": "=",
"value": "2019-07-01 12:00:00"
}
]
}
]
}
}
}
You see there is again operator and rules inside the rules. Any thoughts on how should I be declaring the rules variable to be able to get the expected JSON. I am working to build the JSON on C#.
public static DataSetCommonQuery ConvertToJsonObject(string bracketContents)
{
DataSetCommonQuery commonQuery = new DataSetCommonQuery();
string[] operators = splitWithOperator(bracketContents);
commonQuery.#operator = ReturnOperator(bracketContents);
string output;
do
{
//bracketContesnts = getWhatsInsideBrackets(bracketContents);
for (int i = 0; i < splitWithOperator(bracketContents).Length; i++)
{
var jObject = new JObject();
if(!checkIfBracketsExists(operators[i]))
{
List<string> eachCondition = splitEachCondition(operators[i].Trim());
eachCondition.Add(operators[i].Replace(eachCondition[0], "").Replace(eachCondition[1], "").Trim());// Substring(operators1[i].IndexOf(eachCondition[0]), (operators1[i].IndexOf(eachCondition[1]) - operators1[i].IndexOf(eachCondition[0]))));
jObject.Add("field", eachCondition[0]);
jObject.Add("condition", eachCondition[2]);
jObject.Add("value", eachCondition[1]);
}
else if (checkIfBracketsExists(operators[i]))
{
ConvertToJsonObject(getWhatsInsideBrackets(operators[i]));
}
commonQuery.rules.Add(jObject); //THIS LINE SHOWS ERROR CAN NOT CONVERT JOBJECT TO DataSetCommonQuery
}
} while (checkIfBracketsExists(bracketContents));
return commonQuery;
}
Have you tried site converter like this Json2cssharp
This is really a good question, would suggest you to select Newtonsoft.json and do something like this
public partial class StackOverFlow
{
[JsonProperty("payLoad")]
public PayLoad PayLoad { get; set; }
}
public partial class PayLoad
{
[JsonProperty("DataSetCommonQuery")]
public DataSetCommonQuery DataSetCommonQuery { get; set; }
}
public partial class DataSetCommonQuery
{
[JsonProperty("operator")]
public string Operator { get; set; }
[JsonProperty("rules")]
public DataSetCommonQueryRule[] Rules { get; set; }
}
public partial class DataSetCommonQueryRule
{
[JsonProperty("field", NullValueHandling = NullValueHandling.Ignore)]
public string Field { get; set; }
[JsonProperty("condition", NullValueHandling = NullValueHandling.Ignore)]
public string Condition { get; set; }
[JsonProperty("value", NullValueHandling = NullValueHandling.Ignore)]
public string Value { get; set; }
[JsonProperty("operator", NullValueHandling = NullValueHandling.Ignore)]
public string Operator { get; set; }
[JsonProperty("rules", NullValueHandling = NullValueHandling.Ignore)]
public RuleRule[] Rules { get; set; }
}
public partial class RuleRule
{
[JsonProperty("field")]
public string Field { get; set; }
[JsonProperty("condition")]
public string Condition { get; set; }
[JsonProperty("value")]
public Value Value { get; set; }
}
public partial struct Value
{
public long? Integer;
public string String;
public static implicit operator Value(long Integer) => new Value { Integer = Integer };
public static implicit operator Value(string String) => new Value { String = String };
}
internal static class Converter
{
public static readonly JsonSerializerSettings Settings = new JsonSerializerSettings
{
MetadataPropertyHandling = MetadataPropertyHandling.Ignore,
DateParseHandling = DateParseHandling.None,
Converters =
{
ValueConverter.Singleton,
new IsoDateTimeConverter { DateTimeStyles = DateTimeStyles.AssumeUniversal }
},
};
}
internal class ValueConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Value) || t == typeof(Value?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.Integer:
var integerValue = serializer.Deserialize<long>(reader);
return new Value { Integer = integerValue };
case JsonToken.String:
case JsonToken.Date:
var stringValue = serializer.Deserialize<string>(reader);
return new Value { String = stringValue };
}
throw new Exception();
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (Value)untypedValue;
if (value.Integer != null)
{
serializer.Serialize(writer, value.Integer.Value);
return;
}
if (value.String != null)
{
serializer.Serialize(writer, value.String);
return;
}
throw new Exception();
}
public static readonly ValueConverter Singleton = new ValueConverter();
}

Complex JSON serialization with an odd list issue - Using Json.Net

I have a class that has a list of objects as seen in the first class below.
public class AddComponentToChangeGroupParams
{
[JsonProperty]
static string Id = "1";
public List<Component> Component{ get; set; }
}
public class Component
{
public string Name { get; set; }
public List<ControlName> Controls { get; set; }
}
public class ControlName
{
public string Name { get; set; }
}
I need this to output:
{
"Id": 1,
"Component": {
"Name": "nameOfComponent",
"Controls": [
{"Name": "name of control"},
{"Name": "name of control 2"}
]
},
"Component": {
"Name": "nameOfComponent2",
"Controls": [
{"Name": "name of control"},
{"Name": "name of control 2"}
]
}
}
Any help with this would be greatly appreciated.
Turns out it can be done using JsonConverter:
public class AddComponentToChangeGroupParamsConverter : JsonConverter
{
public override bool CanWrite => true;
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var addComponentToChangeGroupParams = (AddComponentToChangeGroupParams)value;
writer.WriteStartObject();
writer.WritePropertyName("Id");
// I made Id public for simplicity you can use reflection to get value if you want
writer.WriteValue(AddComponentToChangeGroupParams.Id);
foreach (var component in addComponentToChangeGroupParams.Component)
{
writer.WritePropertyName("Component");
serializer.Serialize(writer, component);
}
writer.WriteEndObject();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
}
As you can see you take full responsibility for serializing AddComponentToChangeGroupParams class so you need to write all properties in this converter.
To enable this add [JsonConverter(typeof(AddComponentToChangeGroupParamsConverter))] on AddComponentToChangeGroupParams.

Categories