Serialize Json object with properties dinamycally with dictionary - c#

I need to serialize a response of an object with a dictionary dynamically
left the json examples
I am trying to serialize this response object (request_validator) in a c# class
but this is not working
someone who can help me please, any ideas?
{
"person": {
"testing": "CC",
"simple": "1234545",
"errorNames": {
"id": "655789",
"error": "simple"
},
"errorColor": {
"id": "2",
"error": "error color"
}
}
}
{
"request_validator": [
{
"person.errorNames": [
"error names"
],
"person.errorColor": [
"error color"
]
}
]
}
public class DeserializeResponse{
public Dictionary<string, List<string>> request_validator { get; set; }
}
var error = JsonConvert.DeserializeObject<List<DeserializeResponse>>(content);

You can use Newtonsoft.Json library to get all properties from string array into dictionary
in this case you just need to point to the searched level
using Newtonsoft.Json.Linq;
...
JObject validator = JObject.Parse(content);
IJEnumerable<JToken> validatorTokens = validator.SelectTokens("request_validator")?.Values();
Dictionary<string, List<string>> errors = new Dictionary<string, List<string>>();
if (validatorTokens != null)
{
foreach (JProperty prop in validatorTokens.Values())
{
if (!errors.ContainsKey(prop.Name))
{
errors.Add(prop.Name, new List<string>());
}
errors[prop.Name].Add(prop.Value?.ToString());
}
}

public class DeserializeResponse
{
[JsonPropertyName("request_validator")]
public RequestValidator[] RequestValidator { get; set; }
}
public class RequestValidator
{
[JsonPropertyName("person.errorNames")]
public string[] PersonErrorNames { get; set; }
[JsonPropertyName("person.errorColor")]
public string[] PersonErrorColor { get; set; }
}
...
var error = JsonSerializer.Deserialize<DeserializeResponse>(content);

Related

How to convert json with dynamic root name to C# list

var stringResult = await HttpHelper.PostAsync(batchUrl, content);
I am getting following result as an API response in C# in above stringResult variable after calling above line. I want to convert below "Items" value to C# list. In every result, first GUID is different. How to convert the below result in C# List. I want only Items.
{
"0934009e-6518-4440-91e3-c252d2e2cc4f": {
"Status": 200,
"Headers": {
"Content-Type": "application/json; charset=utf-8"
},
"Content": {
"Items": [{
"Timestamp": "2021-10-18T14:00:00Z",
"Value": 20.7,
"UnitsAbbreviation": "ppm"
}, {
"Timestamp": "2021-10-25T14:00:00Z",
"Value": 15.9,
"UnitsAbbreviation": "ppm"
}, {
"Timestamp": "2021-11-01T14:00:00Z",
"Value": 14.8,
"UnitsAbbreviation": "ppm"
}, {
"Timestamp": "2021-11-08T15:00:00Z",
"Value": 20.1,
"UnitsAbbreviation": "ppm"
}, {
"Timestamp": "2021-11-15T15:00:00Z",
"Value": 19.5,
"UnitsAbbreviation": "ppm"
}, {
"Timestamp": "2021-11-22T15:00:00Z",
"Value": 19.7,
"UnitsAbbreviation": "ppm"
}, {
"Timestamp": "2021-11-29T15:00:00Z",
"Value": 20.4,
"UnitsAbbreviation": "ppm"
}
]
}
}
}
When some part of the json structure is unknown, you need to explore it manually. There are several ways to do it, but I'm used to passing JObject :
var root = JObject.Parse(text);
// Unknown name property, then iterate
// When the object has one property, First is a good fit
var response = root.First as JProperty;
var guid = response.Name;
var itemsJson = response.Value["Content"]["Items"];
var items = itemsJson.ToObject<List<Item>>();
you can try this code
List<Item> items = JObject.Parse(stringResult).Properties()
.First().Value["Content"]["Items"].ToObject<List<Item>>();
public class Item
{
public DateTime Timestamp { get; set; }
public double Value { get; set; }
public string UnitsAbbreviation { get; set; }
}
go to https://json2csharp.com/
convert your json to C# class.
since your root node is dynamic guid, you cannot directly use class structure deserialization but you can use a inner nodes
Pseudo Program
System.Net.ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
WebClient w = new WebClient();
string json = w.DownloadString(new Uri("https://jsonkeeper.com/b/GETO"));
var firstObject = JObject.Parse(json);
var guid = firstObject.Properties().Select(p => p.Name).FirstOrDefault();
//Console.Write(guid);
Root output = firstObject[guid].ToObject<Root>();
Console.Write(output.Content.Items.Count());
The DTO structure
public class Root
{
public int Status { get; set; }
public Headers Headers { get; set; }
public Content Content { get; set; }
}
public class Content
{
public List<Item> Items { get; set; }
}
public class Headers
{
[JsonProperty("Content-Type")]
public string ContentType { get; set; }
}
public class Item
{
public DateTime Timestamp { get; set; }
public double Value { get; set; }
public string UnitsAbbreviation { get; set; }
}
here's fiddle: https://dotnetfiddle.net/w5fO0w
I have used below code to get items -
var resultObjects = AllChildren(JObject.Parse(json))
.First(c => c.Type == JTokenType.Array )
.Children<JObject>();
foreach (JObject result in resultObjects)
{
foreach (JProperty property in result.Properties())
{
}
}
And method is -
private static IEnumerable<JToken> AllChildren(JToken json)
{
foreach (var c in json.Children())
{
yield return c;
foreach (var cc in AllChildren(c))
{
yield return cc;
}
}
}

Is there a better way of decoding a json in C# where the name of the key varies?

I have a response from an external api that looks like this, if i send in properties like this:
{
"color": "blue"
"type": "dog"
}
and then if i enter an invalid value of any of these i get a short error message back and a longer description for the property i sent in that was wrong. So lets say i send in
{
"color": "blue"
"type": "banana"
}
I would get
{
"problem": "invalid pet",
"type": "banana is not a valid type of pet ",
"messageProperties": [
"color",
"type" ]
}
Then if i send in
{
"color": "banana",
"type: "dog"
}
I would get
{
"problem": "wrong pet color",
"color": "banana is not a valid color for a pet",
"messageProperties": [
"color",
"type" ]
}
Is there an easy way of handling this? The best solution i found so far feels overly complex. Is there a better way? I'm using .NET 6
public class MyErrorClass
{
public MyErrorClass(string json)
{
dynamic data = JsonConvert.DeserializeObject<dynamic>(json);
foreach (KeyValuePair<string, JToken> kw in ((JObject)((JContainer)data)))
{
switch (kw.Key)
{
case "context":
context = (string) kw.Value;
break;
case "messageProperties":
{
List<JToken> children = kw.Value.Children().ToList();
messageVariables = children.Values<string>().ToList();
break;
}
default:
error = (string) kw.Value;
break;
}
}
}
public string context { get; set; }
public string error { get; set; }
public List<string> messageVariables { get; set; }
}
One approach is to use JsonExtensionData. For example:
class Resp
{
public string Problem { get; set; }
public List<string> MessageProperties { get; set; }
[JsonIgnore]
public Dictionary<string, string> Extras { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData;
[OnDeserialized]
private void OnDeserialized(StreamingContext context)
{
Extras = _additionalData.ToDictionary(d => d.Key, d => d.Value.ToString());
}
}
Or based on your example:
class MyErrorClass
{
public string Problem { get; set; }
public List<string> MessageProperties { get; set; }
[JsonIgnore]
public string Error { get; set; }
[JsonExtensionData]
private IDictionary<string, JToken> _additionalData;
[OnDeserialized]
private void OnDeserialized(StreamingContext context) => Error = _additionalData?.FirstOrDefault().Value?.ToString();
}
Assuming that you are using Newtonsoft.JSON, you could import Newtonsoft.Json.Linq and perhaps use the following:
if(JObject.Parse(json).ContainsKey("problem"))
For objects you can also use something like:
.GetType().GetProperty("problem") // if != null, then property exists

How to deserialize dynamically named JSON in c#?

I know there is a lot of similar questions and i tried to use it for last 12 h but without result. So please give me any advice how to solved my problem.
My json response look like this:
{
"status": "OK",
"products": {
"dynamic10259668": {
"ean": "4525348924",
"sku": "9384573245",
"name": "name1",
},
"dynamic10630436": {
"ean": "983623203943",
"sku": "9312763245",
"name": "name2"
},
"dynamic10634396": {
"ean": "1002904820",
"sku": "9384763245",
"name": "name3"
},
"dynamic10634398": {
"ean": "3400901100",
"sku": "9312763245",
"name": "name4"
},
"dynamic10634399": {
"ean": "8100103701",
"sku": "454763245",
"name": "name5"
},
"dynamic10634766": {
"ean": "5600904820",
"sku": "9384763245",
"name": "name6"
}
}
}
And models:
public class ProductsList
{
public string status { get; set; }
public ListProducts products { get; set; }
}
public class ListProducts
{
public ListProduct product { get; set; }
}
public class ListProduct
{
public string ean { get; set; }
public string sku { get; set; }
public string name { get; set; }
}
Now i need e.g. Directory<"dynamic10259668", "9384573245"> but don't know how to access to product value. I have try this code:
ProductsList productsList = JsonConvert.DeserializeObject<ProductsList>(response.Content);
foreach (ListProduct singleProduct in productsList.products.product)
{
Console.WriteLine(singleProduct.name);
}
My most common error is:
System.NullReferenceException: Object reference not set to an instance of an object.
You need to use a Dictionary<string, ListProduct>. I believe that will do what you want.
I kept your ListProduct class, but modified ProductsList to look like this:
public class ProductsList
{
public string status { get; set; }
public Dictionary<string, ListProduct> products { get; set; }
}
When I do that, this code properly deserializes your JSON:
var result = JsonConvert.DeserializeObject<ProductsList>(theJson);
You can get to the data for dynamic10259668 using something like:
if (result.products.TryGetValue("dynamic10259668", out var item))
{
Debug.WriteLine($"Name: {item.name}, Ean: {item.ean}, Sku: {item.sku}");
}
It looks like you were hoping for a dictionary of product name to ean. If that is all you need then the following code would work:
dynamic d = JObject.Parse(response.Content);
var productDictionary = new Dictionary<string, string>();
foreach (var product in d.products)
{
productDictionary[product.Name] = (string)product.Value.ean;
}

Json serialization of a nested Dictionary returns { "key": "key1", "value": "value1" }

I'm currently working on a webservice, and I have this behavior that I haven't encountered until today. Here's the class I'm returning:
public class Block
{
public int order { get; set; }
public string title { get; set; }
public Dictionary<string, object> attributes { get; set; }
}
attributes can contain any kind of value: simple type, object, array, etc.
And when I return a Block object through my webservice, here's what I get:
{
"order": 1,
"attributes": [
{
"Key": "key1",
"Value": "value1"
},
{
"Key": "key2",
"Value": "value2"
}
],
"title": "Title"
}
Does anyone know why I'm not simply getting a "key1": "value1" output?
First of all your JSON is not valid, please be aware there are no , after "order": 1 this line.
After this correction you can change your class structure like this
public class Attribute
{
public string Key { get; set; }
public object Value { get; set; }
}
public class Block
{
public int order { get; set; }
public List<Attribute> attributes { get; set; }
public string title { get; set; }
}
this way you will be able to deserialize your JSON, I used simply this website https://json2csharp.com/ for converting your JSON into C# class
As for usage you can do .FirstOrDefault(x=>x.Key=="key1") to get whichever data you want, or if you will process all the list one by one you can simply do attributes.Count
When I do
var x = new Class
{
order = 1,
title = "Title",
attributes = new Dictionary<string, object>
{
{ "key1", "value1" },
{ "key2", "value2" }
}
};
var json = JsonConvert.SerializeObject(x);
Console.WriteLine(json);
I get
{"order":1,"title":"Title","attributes":{"key1":"value1","key2":"value2"}}
Do you have code that serializes your data or does ASP.NET do it for you?

Format each list entry with name JSON

I'm trying to format a List object to JSON in a specific way:
...
{
"MyList": [
"Entry": {
"Id": "1000",
"Name" : "Billy"
}
]
}
...
My problem is that I can't get the "Entry" property name to be written per item in the list.
Here's my C# code:
Entry.cs
[JsonObject(MemberSerialization.OptIn)]
public class Entry
{
[JsonProperty]
public string Id { get; set; }
[JsonProperty]
public string Name { get; set; }
}
List.cs
[JsonObject(MemberSerialization.OptIn)]
public class MyList
{
[JsonProperty]
List<Entry> List = new List<Entry>();
public void Add(Entry param) {
List.Add(param);
}
}
TestController.cs
[HttpPost]
public IHttpActionResult GrabarMarcacion([FromBody] JObject data)
{
MyList lst = new MyList();
lst.Add(new Entry{Id="1000", Name="Billy"});
return Ok(lst);
}
I'm sorry I'm new to JSON and REST, is it even possible to do what I ask? so far I always get something like:
{
"List": {
{
"ID": "1000",
"Name" : "Billy"
},
{
"ID": "1001",
"Name" : "Bob"
}
}
}
I have never used c# but try for Entry.cs :
[JsonObject(MemberSerialization.OptIn)]
public class Entry
{
[JsonProperty]
public object Entry { get; set; }
{
[JsonProperty]
public string Id { get; set; }
[JsonProperty]
public string Name { get; set; }
}
}
From what I understand, you want Entry property name for every item in the list. The simplest way to do this would be by making it a Dictionary:
Renamed Entry.cs to EntryModel.cs
[JsonObject(MemberSerialization.OptIn)]
public class EntryModel
{
[JsonProperty]
public Dictionary<string, string> Entry { get; set; }
}
In List.cs, change the property to MyList. This requires a change to the class name.
[JsonObject(MemberSerialization.OptIn)]
public class MyListModel
{
[JsonProperty]
public List<EntryModel> MyList { get; set; } = new List<EntryModel>();
}
Now in your TestController.cs, you may use:
MyListModel lst = new MyListModel();
lst.MyList.Add(new EntryModel
{
Entry = new Dictionary<string, string> {
{ "Id", "1000" }, { "Name", "Billy" } }
});
lst.MyList.Add(new EntryModel
{
Entry = new Dictionary<string, string> {
{ "Id", "3000" }, {"Name", "ABC" } }
});
This gives the following JSON:
{
"MyList": [
{
"Entry": {
"Id": "1000",
"Name": "Billy"
}
},
{
"Entry": {
"Id": "3000",
"Name": "ABC"
}
}
]
}

Categories