JSON serialization with custom mapping in C# - c#

I want to serialize/deserialize following classes into/from JSON:
public class Employee
{
string name;
Position position;
}
public class Position
{
string positionName;
int salary;
}
The tricky part is that I want to treat Position fields as Employee fields, so JSON would look like this:
{
"name": "John",
"positionName": "Manager",
"salary" : 1000
}
How to achieve this using Json.NET ?

You have either to deserialize it as anonymous object either (recommended ) implement a custom deserialization as stated here:
Merge two objects during serialization using json.net?
Please let us know if there any more questions.
Here's an example (you can find it in the provided link):
public class FlattenJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value,
JsonSerializer serializer)
{
JToken t = JToken.FromObject(value);
if (t.Type != JTokenType.Object)
{
t.WriteTo(writer);
return;
}
JObject o = (JObject)t;
writer.WriteStartObject();
WriteJson(writer, o);
writer.WriteEndObject();
}
private void WriteJson(JsonWriter writer, JObject value)
{
foreach (var p in value.Properties())
{
if (p.Value is JObject)
WriteJson(writer, (JObject)p.Value);
else
p.WriteTo(writer);
}
}
public override object ReadJson(JsonReader reader, Type objectType,
object existingValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override bool CanConvert(Type objectType)
{
return true; // works for any type
}
}

My solution is this
static void Main(string[] args)
{
Position p = new Position();
p.positionName = "Manager";
p.salary = 1000;
Employee e = new Employee();
e.name = "John";
e.position = p;
ResultJson r = new ResultJson();
r.name = e.name;
r.positionName = e.position.positionName;
r.salary = e.position.salary;
var result = JsonConvert.SerializeObject(r);
Console.WriteLine(result);
Console.ReadLine();
}
}
public class Employee
{
public string name { get; set; }
public Position position { get; set; }
}
public class Position
{
public string positionName { get; set; }
public int salary { get; set; }
}
public class ResultJson
{
public string name { get; set; }
public string positionName { get; set; }
public int salary { get; set; }
}
use seperate model for result

You can use this code with NewtonSoft.Json library
[JsonObject]
public class Employee
{
[JsonProperty("name")]
string name;
[JsonProperty("positionName")]
string positionName;
[JsonProperty("salary")]
int salary;
}
Use One class instead 2, or realize own parser

try in this way.
{
"name": "John",
"position":
[{
"positionName": "Manager",
"salary" : 1000
}]
}

Related

Deserialize an abstract class that has only a getter using Newtonsoft

I'm trying to deseralize JSON I'm getting:
[
{
"Name":"0",
"Health":0,
"TypeName":"SpellInfo",
"Info":{
"Effect":1,
"EffectAmount":4
}
},
{
"Name":"1",
"Health":0,
"TypeName":"MonsterInfo",
"Info":{
"Health":10,
"AttackDamage":10
}
},
...
...
]
Created a class to handle the JSON:
[System.Serializable]
public class CardDataStructure
{
public string Name;
public int Health;
public string TypeName;
public Info Info;
}
I managed to get all the info I needed but the Info. From the research I did, I created a JsonConverter from a link - https://blog.codeinside.eu/2015/03/30/json-dotnet-deserialize-to-abstract-class-or-interface/
Which is actually pretty close,
public class InfoConvert: JsonConverter
{
public override bool CanConvert(Type objectType)
{
return (objectType == typeof(Info));
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jo = JObject.Load(reader);
if (jo.ToString().Contains("Effect"))
{
if (jo["Effect"].Value<string>() is string)
return jo.ToObject<SpellInfo>(serializer);
}
if (jo.ToString().Contains("Health"))
{
if (jo["Health"].Value<string>() is string)
return jo.ToObject<MonsterInfo>(serializer);
}
return null;
}
}
(It would have been better to find it by 'typename' but I couldn't figure out how to do that, so went with something simple)
When checking 'jo', the properties are there and go to the correct class yet once out of the converter I get default properties and not the once the converter showed.
I can't find the link but on the Newtonsoft doc it said somewhere there's a problem with deserializing an abstract class and if the abstract class doesn't have a public setter.
Both monsterinfo and spellinfo inherit from info:
[Serializable]
public abstract class Info
{
}
The monsterinfo and spellinfo look basically the same. Problem is they don't have a public setters and I cannot change them right now.
{
[Serializable]
public class MonsterInfo: Info
{
[SerializeField]
private int m_Health;
public int Health => m_Health;
[SerializeField]
private int m_AttackDamage;
public int AttackDamage => m_AttackDamage;
}
}
So, when trying to deseralize the JSON:
string contents = File.ReadAllText(source);
contents = "{\"cards\":" + contents + "}";
JsonConverter[] converters = { new InfoConvert() };
cardsData = JsonConvert.DeserializeObject<Cards>(contents, new JsonSerializerSettings() {
Converters = converters, NullValueHandling = NullValueHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto});
*Cards is a list of CardDataStructure
Is it even possible to get the data in Info without giving them a public setter?
Best I got is all the data inside the JSON and an empty Monster/Spell Info.
At the end I just need to parse the json I'm getting, but while the 'name', 'health', 'typeinfo' are parsed correctly, info is always an empty object filled with 0s.
Edit: Corrected some things.
You should do that like this dude:
A marker interface for detecting the type or deserializing
A container class
Dto classes
//marker interface
public interface Info { }
public class HealthInfo : Info
{
public int MoreHealth { set; get; }
public int AttackDamage { set; get; }
}
public class SpellInfo : Info
{
public int Effect { set; get; }
public int EffectAmount { set; get; }
}
public class Card<T> where T : Info
{
public Card(string name, int health, T info)
{
this.Info = info;
this.Name = name;
this.Health = health;
}
public T Info { private set; get; }
public string Name { set; get; }
public int Health { set; get; }
}
public class InfoConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
if (objectType == typeof(Card<Info>))
{
return true;
}
return false;
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JObject jObject = JObject.Load(reader);
if (jObject.ContainsKey("TypeName"))
{
string typeName = jObject["TypeName"]?.ToString()?.Trim()?.ToLower();
if (typeName?.Equals("monsterinfo") == true)
{
Card<HealthInfo> deseerialized = jObject.ToObject<Card<HealthInfo>>();
return new Card<Info>(deseerialized.Name, deseerialized.Health, deseerialized.Info);
}
if (typeName?.Equals("spellinfo") == true)
{
string json = jObject.ToString();
Card<SpellInfo> deseerialized = jObject.ToObject<Card<SpellInfo>>();
return new Card<Info>(deseerialized.Name, deseerialized.Health, deseerialized.Info);
}
}
return null;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And you should execute:
List<Card<Info>> list = JsonConvert.DeserializeObject<List<Card<Info>>>(jsonText, new InfoConverter());

JSON complex type that can be an object or an array of objects [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 3 years ago.
I am trying to process an object that can be either an array of object or just the object. Using the code below only works when the naics is an object and not an array. What am I doing wrong?
Here is the shortest example I can come up with:
[{
"section": "52.219-1.b",
"naics": [{
"naicsName": "Engineering Services",
"isPrimary": true,
"ExcpCounter": 1,
"isSmallBusiness": "Y",
"naicsCode": 541330
},
{
"naicsName": "Military and Aerospace Equipment and Military Weapons",
"isPrimary": true,
"ExcpCounter": 2,
"isSmallBusiness": "Y",
"naicsCode": 541330
}
]
},
{
"section": "52.219-1.b",
"naics": {
"naicsName": "Janitorial Services",
"isPrimary": true,
"isSmallBusiness": "Y",
"naicsCode": 561720
}
}
]
I will only have one of the types but I forced two in an array to force it into Quick Type.
My classes are:
[JsonProperty("naics", NullValueHandling = NullValueHandling.Ignore)]
public AnswerNaics Naics { get; set; }
public partial struct AnswerNaics
{
public AnswerNaic Naic;
public AnswerNaic[] NaicArray;
public static implicit operator AnswerNaics(AnswerNaic Naic) => new AnswerNaics { Naic = Naic };
public static implicit operator AnswerNaics(AnswerNaic[] NaicArray) => new AnswerNaics { NaicArray = NaicArray };
}
public partial class AnswerNaic
{
[JsonProperty("naicsName")]
public string NaicsName { get; set; }
[JsonProperty("hasSizeChanged")]
public string HasSizeChanged { get; set; }
[JsonProperty("isPrimary")]
public bool IsPrimary { get; set; }
[JsonProperty("ExcpCounter", NullValueHandling = NullValueHandling.Ignore)]
public long? ExcpCounter { get; set; }
[JsonProperty("isSmallBusiness")]
public string IsSmallBusiness { get; set; }
[JsonProperty("naicsCode")]
public string NaicsCode { get; set; }
}
internal class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(AnswerNaics) || t == typeof(AnswerNaics?);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
switch (reader.TokenType)
{
case JsonToken.StartObject:
var objectValue = serializer.Deserialize<AnswerNaic>(reader);
return new AnswerNaics { Naic = objectValue };
case JsonToken.StartArray:
var arrayValue = serializer.Deserialize<AnswerNaic[]>(reader);
return new AnswerNaics { NaicArray = arrayValue };
}
throw new Exception("Cannot unmarshal type AnswerNaics");
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
var value = (AnswerNaics)untypedValue;
if (value.NaicArray != null)
{
serializer.Serialize(writer, value.NaicArray);
return;
}
if (value.Naic != null)
{
serializer.Serialize(writer, value.Naic);
return;
}
throw new Exception("Cannot marshal type Naics");
}
public static readonly NaicsConverter Singleton = new NaicsConverter();
}
I have more object or array nodes, but I am just trying to figure out one to be able to apply to all of them.
Since you cannot change the incoming JSON, you're going to need a custom converter instead. Something like this for example:
public class NaicsConverter : JsonConverter
{
public override bool CanConvert(Type t) => t == typeof(Naics);
public override object ReadJson(JsonReader reader, Type t, object existingValue, JsonSerializer serializer)
{
var naics = new Naics();
switch (reader.TokenType)
{
case JsonToken.StartObject:
// We know this is an object, so serialise a single Naics
naics.Add(serializer.Deserialize<Naic>(reader));
break;
case JsonToken.StartArray:
// We know this is an object, so serialise multiple Naics
foreach(var naic in serializer.Deserialize<List<Naic>>(reader))
{
naics.Add(naic);
}
break;
}
return naics;
}
public override void WriteJson(JsonWriter writer, object untypedValue, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
And the supporting classes:
public class Root
{
public string Section { get; set; }
[JsonConverter(typeof(NaicsConverter))]
public Naics Naics { get; set; }
}
// This isn't ideal, but it's quick and dirty and should get you started
public class Naics : List<Naic>
{
}
public class Naic
{
public string NaicsName { get; set; }
public bool IsPrimary { get; set; }
public string IsSmallBusiness { get; set; }
public long NaicsCode { get; set; }
}
And finally, to deserialise:
var settings = new JsonSerializerSettings {Converters = {new NaicsConverter()}};
var root = JsonConvert.DeserializeObject<Root[]>(Json, settings);
Now your object will get serialised into the list, but as a single item.
You can solve this using a dynamic field in your class.
Consider this JSON:
[
{
"field1": "val1",
"nested": [
{
"nestedField": "val2"
},
{
"nestedField": "val3"
}
]
},
{
"field1": "val4",
"nested":
{
"nestedField": "val5"
}
}
]
nested field is first an array with 2 objects and in the second appearance a single object. (similar to the JSON you posted)
So the class representation would look like:
public class RootObject
{
public string field1 { get; set; }
public dynamic nested { get; set; }
public List<NestedObject> NestedObjects
{
get
{
if(nested is JArray)
{
return JsonConvert.DeserializeObject<List<NestedObject>>(nested.ToString());
}
var obj = JsonConvert.DeserializeObject<NestedObject>(nested.ToString());
return new List<NestedObject> { obj };
}
}
}
public class NestedObject
{
public string nestedField { get; set; }
}
The Deserialization code is trivial using Newtonsoft JSON:
var objectList = JsonConvert.DeserializeObject<List<RootObject>>("some_json");
foreach(var v in objectList)
{
foreach(var n in v.NestedObjects)
{
Console.WriteLine(n.nestedField);
}
}
The only change is an implementation of NestedObjects ready only property. It check if the dynamic object is JArray or object. In any case, it returns a List of nested objects.

Convert C# class to Json with customized structure

we have our C# class as below
public class PrimaryContact
{
public string PrefixTitle { get; set; }
public string SurName { get; set; }
public string GivenName { get; set; }
}
we need to serialise to the json object as
"primaryContact": {
"prefixTitle": {
"value": "mrs"
},
"surName": "abcd",
"givenName": "abcd"
}
Please note the prefixTitle is intended with value. for some selected attributes we need to serialize like this. Also, we need to read from JSON and deserialise into the class. Is there any generic best approach we can follow by decorating the elements so that we can achieve this result?
As you have tagged your question with json.net, you can do this by applying a custom JsonConverter to the "selected attributes" that should be nested inside a {"value" : ... } object when serialized.
First, define the following converter:
public class NestedValueConverter<T> : JsonConverter
{
class Value
{
public T value { get; set; }
}
public override bool CanConvert(Type objectType)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
switch (reader.MoveToContent().TokenType)
{
case JsonToken.Null:
return null;
default:
return serializer.Deserialize<Value>(reader).value;
}
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
serializer.Serialize(writer, new Value { value = (T)value });
}
}
public static partial class JsonExtensions
{
public static JsonReader MoveToContent(this JsonReader reader)
{
if (reader.TokenType == JsonToken.None)
reader.Read();
while (reader.TokenType == JsonToken.Comment && reader.Read())
;
return reader;
}
}
Now, apply it the "selected attributes" of PrimaryContact as follows:
public class PrimaryContact
{
[JsonConverter(typeof(NestedValueConverter<string>))]
public string PrefixTitle { get; set; }
public string SurName { get; set; }
public string GivenName { get; set; }
}
And you will be able to deserialize and serialize as follows:
var settings = new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver(),
};
var root = JsonConvert.DeserializeObject<RootObject>(jsonString, settings);
var json2 = JsonConvert.SerializeObject(root, Formatting.Indented, settings);
Notes:
As the converter is intended to be applied directly using the attributes [JsonConverter(typeof(NestedValueConverter<...>))] or [JsonProperty(ItemConverterType = typeof(NestedValueConverter<...>))], CanConvert, which is only called when the converter is included in settings, is not implemented.
The converter is generic in case you need to nest non-string values.
Sample fiddle here.
Here Prefix Title Also a class not a string.
Here your class want to look like this.
public class PrimaryContact
{
public PrefixTitle prefixTitle{ get; set; }
public string surName{ get; set; }
public string givenName{ get; set; }
}
public class PrefixTitle {
public string value {get; set;}
}
Install Newtonsoft.json libraby file to your project : ->
Open Package manager console in Tools NuGet Package and paste it then hit enter.
Install-Package Newtonsoft.Json -Version 12.0.1
Convert a Class to Json :
string output = JsonConvert.SerializeObject(classname);
Convert a Json to Object :
Here object denotes a Class
Object output = JsonConvert.DeSerializeObject<object>(jsonString);
Here You can find optimized code you can use in your project directly :
public static string getJsonFromClass(object objectName){
return JsonConvert.SerializeObject(object);
}
public static T getObjectFromJson<T>(string jsonString){
T t = default(T);
try{
t = JsonConvert.DeSerializeObject<T>(classname);
}catch(Exception e){
Debug.WriteLine(e.Message);
}
return t;
}
You can use this Method to achieve your output by :
string jsonData = getJsonFromClass(Prefix);
string JsonString = "<here your json string>";
Prefix getObjectFromJson = getObjectFromJson<Prefix>(JsonString);
thats all ..
I hope this can help for you..
You can achieve this by changing your model like:
public class PrimaryContact
{
public Prefix PrefixTitle { get; set; }
public string SurName { get; set; }
public string GivenName { get; set; }
}
public class Prefix
{
public string Value { get; set; }
}
Then
Newton.Json.JsonConvert.DeserializeObject<PrimaryContact>();
You need to write a custom serializer for your object. Here is an example to show how you can do this with extending JsonConverter and using some custom attribute to determine if your object/property should wrapped:
[WrapperAttribute(Key = "primaryContact")]
public class PrimaryContact
{
[WrapperAttribute(Key= "prefixTitle")]
public string PrefixTitle { get; set; }
public string SurName { get; set; }
public string GivenName { get; set; }
}
public class WrapperAttribute : Attribute
{
public string Key { get; set; }
}
public class WrapperSerializer : JsonConverter<PrimaryContact>
{
public override void WriteJson(JsonWriter writer, PrimaryContact value, JsonSerializer serializer)
{
Type type = value.GetType();
JObject root = new JObject();
foreach (var property in type.GetAllProperties())
{
if (property.HasAttribute<WrapperAttribute>())
{
JProperty wrappedProperty = new JProperty(property.GetAttribute<WrapperAttribute>().Key);
JObject wrappedChild = new JObject();
wrappedProperty.Value = wrappedChild;
JProperty wrappedChildProperty = new JProperty("value");
wrappedChildProperty.Value = JToken.FromObject(property.GetValue(value));
wrappedChild.Add(wrappedChildProperty);
root.Add(wrappedProperty);
}
else
{
var childProperty = new JProperty(property.Name);
childProperty.Value = JToken.FromObject(property.GetValue(value));
root.Add(childProperty);
}
}
if (type.HasAttribute<WrapperAttribute>())
{
JObject wrappedRoot = new JObject();
var wrappedProperty = new JProperty(type.GetAttribute<WrapperAttribute>().Key);
wrappedProperty.Value = root;
wrappedRoot.Add(wrappedProperty);
wrappedRoot.WriteTo(writer);
}
else
{
root.WriteTo(writer);
}
}
public override PrimaryContact ReadJson(JsonReader reader, Type objectType, PrimaryContact existingValue, bool hasExistingValue,
JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
in main :
PrimaryContact contact = new PrimaryContact();
contact.GivenName = "test name";
contact.PrefixTitle = "test title";
contact.SurName = "test surname";
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.Converters.Add(new WrapperSerializer());
var serialized = JsonConvert.SerializeObject(contact, settings);
output :
{
"primaryContact": {
"prefixTitle": {
"value": "test title"
},
"SurName": "test surname",
"GivenName": "test name"
}
}

Use Json Converter to add properties to Dictionary

I am deserializing some JSON endpoints to C# Objects via JArray.Parse() and have come across a schema I'm not sure how to handle. The nUnavailResReasonsMap Object below has a dynamic number of name/value pairs; all of the name values will be integers:
{
"id": "Customer Service",
"operation": "UPDATE",
"VoiceIAQStats": {
"id": 139,
"esdId": 139,
...
"nUnavailResources": 2,
"nUnavailResReasonsMap": {
"4": 1,
"9": 1
},
"nWorkResources": 0,
"nSelectedResources": 0,
...
"nSLAPercentageHighThreshold": 0
}
}
And here are my C# Objects:
//Root Level
public class QueueStats
{
public string Id { get; set; }
public string Operation { get; set; }
public VoiceIaqStats VoiceIaqStats { get ; set ; }
}
public class VoiceIaqStats
{
public int Id { get; set; }
public int EsdId { get; set; }
...
public int NUnavailResources { get; set; }
public NUnavailResReasonsMaps NUnavailResReasonsMap { get ; set ; }
public int NWorkResources { get; set; }
public int NSelectedResources { get; set; }
...
public int NSlaPercentageHighThreshold { get; set; }
}
[JsonConverter( typeof( QueueStatsConverter))]
public class NUnavailResReasonsMaps
{
public Dictionary<string ,int > NUnavailResReasonsMap { get; set; }
}
Per another SO Post, I have setup the below Json Converter, which is getting called but I'm not sure how to get the values into the Dictionary defined above.
public class QueueStatsConverter : JsonConverter
{
public override bool CanConvert( Type objectType)
{
return objectType.IsClass;
}
public override bool CanWrite => false;
public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var instance = objectType.GetConstructor(Type .EmptyTypes)?.Invoke(null);
var props = objectType.GetProperties();
var jo = JObject.Load(reader);
foreach ( var jp in jo.Properties())
{
//I can see the properties, but how do I add them to my existing dictionary?
}
return instance;
}
public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Is there a way to make this work or do I need to change my approach?
You don't need a special converter at all, just remove the NUnavailResReasonsMaps class and use a Dictionary<int, int> instead:
public class VoiceIaqStats
{
//snip
public Dictionary<int, int> NUnavailResReasonsMap { get; set; }
//snip
}
And the conversion will work out of the box:
var result = JsonConvert.DeserializeObject<QueueStats>(json);

Deserialize JSON to list in C#

json - http://pastebin.com/Ss1YZsLK
I need to get market_hash_name values to list. I can receive first value so far:
using (WebClient webClient = new System.Net.WebClient()) {
WebClient web = new WebClient();
var json = web.DownloadString(">JSON LINK<");
Desc data = JsonConvert.DeserializeObject<Desc>(json);
Console.WriteLine(data.rgDescriptions.something.market_hash_name);
}
public class Desc {
public Something rgDescriptions { get; set; }
}
public class Something {
[JsonProperty("822948188_338584038")]
public Name something { get; set; }
}
public class Name {
public string market_hash_name { get; set; }
}
How can I get all if them?
Since there is no array inside the rgDescriptions but some randomly named looking properties I think you would need a custom JsonConverter. The following console application seems to be working and displaying the market_hash_names correctly:
class Program
{
static void Main(string[] args)
{
string json = File.ReadAllText("Sample.json");
Desc result = JsonConvert.DeserializeObject<Desc>(json);
result.rgDescriptions.ForEach(s => Console.WriteLine(s.market_hash_name));
Console.ReadLine();
}
}
public class Desc
{
[JsonConverter(typeof(DescConverter))]
public List<Something> rgDescriptions { get; set; }
}
public class Something
{
public string appid { get; set; }
public string classid { get; set; }
public string market_hash_name { get; set; }
}
class DescConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return objectType == typeof(Something[]);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var descriptions = serializer.Deserialize<JObject>(reader);
var result = new List<Something>();
foreach (JProperty property in descriptions.Properties())
{
var something = property.Value.ToObject<Something>();
result.Add(something);
}
return result;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Output:
Genuine Tactics Pin
Silver Operation Breakout Coin
Operation Phoenix Challenge Coin
Operation Bravo Challenge Coin

Categories

Resources