I have a simple GitHub payload incoming to my ASP.NET Core application and I would like to know how can I map the payload I receive to my DTO.
Example DTO
public class GithubPayload
{
public string Action { get; set; } // action
public string Name { get; set; } // pull_request.title
}
Example payload
{
"action": "deleted",
"pull_request": {
"title": "Fix button"
}
}
You can use JsonProperty attribute on Action and a custom converter on the Name that can interpret nested properties. check Json.Net's JsonConverter
public class GithubPayload {
[JsonProperty("action")]
public string Action { get; set; }
[JsonConverter(typeof(NestedConverter), "pull_request.title")]
public string Name { get; set; }
}
Where NestedConverter is a custom JsonConverter that will read a nested property
public class NestedConverter : JsonConverter {
private readonly string path;
public NestedConverter (string path) {
this.path = path;
}
//...to do
}
Update:
Using a JsonConverter for converting the payload itself actually works as well
public class GithubPayloadConverter : JsonConverter {
public override bool CanConvert(Type objectType) {
return objectType == typeof(GithubPayload);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
dynamic data = JObject.Load(reader);
var model = new GithubPayload {
Action = data.action,
Name = data.pull_request.title
};
return model;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
throw new NotImplementedException();
}
}
and decorating the class itself
[JsonConverter(typeof(GithubPayloadConverter))]
public class GithubPayload {
public string Action { get; set; }
public string Name { get; set; }
}
Deserialization is simply
string json = #"{ 'action': 'deleted', 'pull_request': { 'title': 'Fix button' } }";
var payload = JsonConvert.DeserializeObject<GithubPayload>(json);
Just an idea:
using Microsoft.AspNetCore.Hosting;
using Newtonsoft.Json;
using System.IO;
namespace WebApplication1
{
public class Program
{
public class GithubPayload
{
public string Action { get; set; } // action
public string Name { get; set; } // pull_request.title
}
public static void Main(string[] args)
{
string json = #"{
""action"": ""deleted"",
""pull_request"": {
""title"": ""Fix button""
}
}";
dynamic obj = JsonConvert.DeserializeObject(json);
GithubPayload entity = new GithubPayload();
entity.Action = obj.action;
entity.Name = obj.pull_request.title;
..................
}
}
}
Tested this solution, it works.
Related
I have the below JSON coming from API
{
"rates": {
"EURGBP": {
"rate": 0.871625,
"timestamp": 1619193484
},
"USDJPY": {
"rate": 107.939463,
"timestamp": 1619193484
}
}
}
I want to create classes to deserialize this JSON. If I do JSON to C# conversion I get the below C# classes:
public class Rates
{
public EURGBP EURGBP { get; set; }
public USDJPY USDJPY { get; set; }
}
public class EURGBP
{
public double rate { get; set; }
public int timestamp { get; set; }
}
public class USDJPY
{
public double rate { get; set; }
public int timestamp { get; set; }
}
The problem is there might be more objects coming in rates object. Like
"NZDUSD": {
"rate": 0.718962,
"timestamp": 1619198884
}
In this case I won't be having a class NZDUSD to deserialize the new object.
So can I have a generic class using which I can deserialize the objects under rates since they will be having the common properties?
OR
Can I convert those objects under rates into List or Dictionary while or after deserializing?
You could create an object model like this using a dictionary:
public class Result
{
public IDictionary<string, Rate> Rates { get; set; }
}
public class Rate
{
public double rate { get; set; }
public int timestamp { get; set; }
}
and then deserialize using Newtonsoft:
var values = Newtonsoft.Json.JsonConvert.DeserializeObject<Result>(json);
So can I have a generic class using which I can deserialize the objects under rates since they will be having the common properties?
There are a few different way's you can accomplish this, one of which was already offered by Connell. Another way you can do this is by creating your own JsonConverter. Below I will show you how you can do this.
-Create a new class called: CurrencyConverter make sure it's like below:
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
public class CurrencyConverter : JsonConverter
{
public override bool CanConvert(Type objectType) => typeof(List<Rate>).IsAssignableFrom(objectType);
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
List<Rate> rates = new List<Rate>();
if (reader.TokenType == JsonToken.StartObject)
{
JToken token = JToken.Load(reader);
IEnumerable<JToken> tokenChildren = token.Root.First?.First?.Children();
foreach (JToken child in tokenChildren)
{
double dblRate = GetValue(child.First, "rate", 0d);
int intTimeStamp = GetValue(child.First, "timestamp", 0);
rates.Add(new Rate() { RateName = ((JProperty)child).Name, rate = dblRate, Timestamp = intTimeStamp });
}
}
return rates;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public T GetValue<T>(JToken jToken, string key, T defaultValue = default(T))
{
dynamic ret = jToken[key];
if (ret == null) return defaultValue;
if (ret is JObject) return JsonConvert.DeserializeObject<T>(ret.ToString());
return (T)ret;
}
}
-Next create a new class called Rate:
public class Rate
{
public string RateName { get; set; }
public double rate { get; set; }
public int Timestamp { get; set; }
}
-Finally deserialize your json:
JsonSerializerSettings jsonSerializerSettings = new JsonSerializerSettings();
jsonSerializerSettings.Converters.Add(new CurrencyConverter());
var jsonOutput = JsonConvert.DeserializeObject<List<Rate>>(json, jsonSerializerSettings);
Using the json you provided, I got back a list that had two Rate objects.
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());
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"
}
}
I have a Web Api Controller like this one :
public IHttpActionResult Create(PaymentDTO Payment)
My DTOs are:
public class PaymentDTO
{
public int Id { get; set; }
public string type { get; set; }
public IEnumerable<TransactionDTO> Transactions { get; set; }
}
public class TransactionDTO
{
public int Id { get; set; }
public string Description { get; set; }
public string CreateTime { get; set; }
public string UpdateTime { get; set; }
}
public class SaleDTO : TransactionDTO
{
public string Total { get; set; }
public string Currency{ get; set; }
}
public class OrderDTO : TransactionDTO
{
public string State {get;set;}
}
I receive the following JSON formatted data :
{
"Type": "sale",
"Id": 101,
"transactions": [
{
"Total": "30.50",
"Currency": "USD",
"Description": "transaction description"
}
]
}
I want JSON.net to instantiate either a IEnumerable<SaleDTO> or IEnumerable<OrderDTO> based on the Type Property.
I could've used a custom type converter, but only if Type property was in TransactionDTO. But I want the Type property to be in the parent object (PaymentDTO)
Thank you in advance for your help.
You can do this with a custom JsonConverter on the PaymentDTO class:
public class PaymentDTOConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(PaymentDTO).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
return null;
var obj = JObject.Load(reader);
var payment = (PaymentDTO)existingValue ?? new PaymentDTO();
// Extract the transactions.
var transactions = obj.Property("transactions") ?? obj.Property("Transactions");
if (transactions != null)
transactions.Remove();
// Populate the remaining regular properties.
using (var subReader = obj.CreateReader())
serializer.Populate(subReader, payment);
if (transactions != null)
{
// Deserialize the transactions list.
var type = PaymentDTO.GetTransactionDTOType(payment.type) ?? typeof(TransactionDTO);
using (var subReader = transactions.Value.CreateReader())
// Here we are taking advantage of array covariance.
payment.Transactions = (IEnumerable<TransactionDTO>)serializer.Deserialize(subReader, type.MakeArrayType());
}
return payment;
}
public override bool CanWrite { get { return false; } }
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Then apply it to your PaymentDTO class as follows:
[JsonConverter(typeof(PaymentDTOConverter))]
public class PaymentDTO
{
static Dictionary<string, Type> namesToTransactions;
static Dictionary<Type, string> transactionsToNames = new Dictionary<Type, string>
{
{ typeof(SaleDTO), "sale" },
{ typeof(OrderDTO), "order" },
};
static PaymentDTO()
{
namesToTransactions = transactionsToNames.ToDictionary(p => p.Value, p => p.Key);
}
public static string GetTransactionDTOTypeName<TTransactionDTO>() where TTransactionDTO : TransactionDTO
{
string name;
if (transactionsToNames.TryGetValue(typeof(TTransactionDTO), out name))
return name;
return null;
}
public static Type GetTransactionDTOType(string name)
{
Type type;
if (namesToTransactions.TryGetValue(name, out type))
return type;
return null;
}
public int Id { get; set; }
public string type { get; set; }
[JsonProperty("transactions")]
public IEnumerable<TransactionDTO> Transactions { get; set; }
}
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