I got some Json, that looks like this:
[
{
"starttime": "2020-02-27T14:30:00Z",
"endtime": "2020-02-27T14:40:00Z"
},
{
"Temp": {
"value": 3
},
"Pressure": {
"value": 29
},
"Humidity": {
"value": 85
}
}
]
I would like to deserialize it onto a object on the form:
public class Sample {
public string Name {get; set;}
public int Value {get;set;}
}
and then get 3 instances where name is set to either Temp, Pressure, Humidity, and Value set to 3, 29, 85
I don't really care about the start-/endtime part.
Any help would be greatly appreciated...
/Søren
Update:
Came up with this myself:
var tmp = JsonConvert.DeserializeObject<JArray>(content);
var samples = tmp.
SelectMany(x => ((JToken) x).Children())
.Where(x => !((JProperty) x).Name.Contains("time"))
.Select(x =>
{
var tmp2 = x.First.ToObject<Sample>();
tmp2.name = ((JProperty) x).Name;
return tmp2;
})
.ToList();
but I think Pavel's solution below, is more readable....
You can use Json.Linq to get a list of Sample objects from your json. Parse json to JArray instance, then enumerate all properties of the last object to get the names and values
var array = JArray.Parse(json);
var samples = ReadSamples(array.Last());
foreach (var sample in samples)
{
Console.WriteLine($"{sample.Name} {sample.Value}");
}
IEnumerable<Sample> ReadSamples(JToken data)
{
foreach (JProperty item in data)
{
yield return new Sample()
{
Name = item.Name,
Value = item.Value["value"]?.Value<int>() ?? 0
};
}
}
The output will be the following
Temp 3
Pressure 29
Humidity 85
It's also possible to do the same using System.Text.Json API, which is available from .NET Core 3.x
Per your posted JSON, you would get a model like below. Use http://json2csharp.com/#
public class Temp
{
public int value { get; set; }
}
public class Pressure
{
public int value { get; set; }
}
public class Humidity
{
public int value { get; set; }
}
public class RootObject
{
public DateTime starttime { get; set; }
public DateTime endtime { get; set; }
public Temp Temp { get; set; }
public Pressure Pressure { get; set; }
public Humidity Humidity { get; set; }
}
If your JSON is not dynamic, creating classes which model your JSON is a good idea.
An easy way is to Copy JSON to clipboard -> Open Visual Studio -> Edit -> Paste Special -> Paste JSON as classes.
This should give you the following classes:
public class Class1
{
public DateTime starttime { get; set; }
public DateTime endtime { get; set; }
public Temp Temp { get; set; }
public Pressure Pressure { get; set; }
public Humidity Humidity { get; set; }
}
public class Temp
{
public int value { get; set; }
}
public class Pressure
{
public int value { get; set; }
}
public class Humidity
{
public int value { get; set; }
}
And now you can deserialize the JSON array to List<Class1>using the Newtonsoft.Json NuGet package:
using Newtonsoft.Json;
using System.Collections.Generic;
...
string json = #"[
{
""starttime"": ""2020 - 02 - 27T14: 30:00Z"",
""endtime"": ""2020-02-27T14:40:00Z""
},
{
""Temp"": {
""value"": 3
},
""Pressure"": {
""value"": 29
},
""Humidity"": {
""value"": 85
}
}
]";
var myObject = JsonConvert.DeserializeObject<List<Class1>>(json);
Related
I have this JSON object returned from API:
[
{
"batchId": 789,
"debtId": 1841,
"dateAdded": "2021-07-27T16:01:39.41",
"debtCategoryId": 2,
"agreementNumber": 78262155,
"clientNumber": 1068055,
"clientName": "Client Two"
},
{
"batchId": 866,
"debtId": 1918,
"dateAdded": "2021-08-25T14:47:18.13",
"debtCategoryId": 2,
"agreementNumber": 1000140792,
"clientNumber": 11213287,
"clientName": "Client One"
}
]
I'm trying to convert that to a C# object which has this structure:
public class DebtConfirmationResponse
{
public List<DebtConfirmation> DebtConfirmations { get; set; }
}
Where DebtConfirmation has these properties:
public class DebtConfirmation
{
public int BatchId { get; set; }
public int DebtId { get; set; }
public string DateAdded { get; set; }
public int DebtCategoryId { get; set; }
public string AgreementNumber { get; set; } = string.Empty;
public string ClientNumber { get; set; } = string.Empty;
public string ClientName { get; set; } = string.Empty;
}
The error I'm getting is:
the json value could not be converted to the name of the model path $
linenumber 0 bytepositioninline 1
Is there anything wrong with the way how the model is being setup?
I also tried converting to the same model with batch id only as a property and I got the same message.
You define AgreementNumber, ClientNumber as strings in your C# code, but this properties is numbers in json, so you have to define it as longs.
And the another point is that you don't need a wrapper around DebtConfirmation class. Deserealize your json into ICollection, IList or just List of DebtConfirmation objects.
I used the quicktype.io for retrieving C# classes from json example you provide. This is very helpful for those who doesn't want to manually generate the models for their JSON strings.
Here is the code sample.
The output is:
789
866
using System.Text.Json;
using System.Text.Json.Serialization;
string json = "[\n {\n \"batchId\": 789,\n \"debtId\": 1841,\n \"dateAdded\": \"2021-07-27T16:01:39.41\",\n \"debtCategoryId\": 2,\n \"agreementNumber\": 78262155,\n \"clientNumber\": 1068055,\n \"clientName\": \"Client Two\"\n },\n {\n \"batchId\": 866,\n \"debtId\": 1918,\n \"dateAdded\": \"2021-08-25T14:47:18.13\",\n \"debtCategoryId\": 2,\n \"agreementNumber\": 1000140792,\n \"clientNumber\": 11213287,\n \"clientName\": \"Client One\"\n }\n]";
var data = JsonSerializer.Deserialize<ICollection<DebtConfirmation>>(json);
foreach (DebtConfirmation current in data)
{
Console.WriteLine(current.BatchId);
}
public partial class DebtConfirmation
{
[JsonPropertyName("batchId")]
public long BatchId { get; set; }
[JsonPropertyName("debtId")]
public long DebtId { get; set; }
[JsonPropertyName("dateAdded")]
public DateTimeOffset DateAdded { get; set; }
[JsonPropertyName("debtCategoryId")]
public long DebtCategoryId { get; set; }
[JsonPropertyName("agreementNumber")]
public long AgreementNumber { get; set; }
[JsonPropertyName("clientNumber")]
public long ClientNumber { get; set; }
[JsonPropertyName("clientName")]
public string ClientName { get; set; }
}
You can use #GeorgyTarasov's answer. But it is definitely not the simplest option.
You can simply use JsonNamingPolicy.CamelCase.
Then you would simply do...
var options = new JsonSerializerOptions
{
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
};
var ret = JsonSerializer.Deserialize<DebtConfirmation>(payload, options);
If you are using AspNet Core, you can register the option here
services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.DictionaryKeyPolicy = JsonNamingPolicy.CamelCase;
});
I'm trying to read this JSON array:
[{"name":"lc_cash","slot":1,"info":"","type":"item","amount":591},{"name":"advancedlockpick","slot":2,"info":[],"type":"item","amount":19}]
This is my code:
File Inventory.cs
class Item {
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("slot")]
public int Slot { get; set; }
[JsonProperty("info")]
public object Info { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("amount")]
public int Amount { get; set; }
}
class Inventory
{
public List<Item> Item { get; set; }
}
File Form1.cs
Inventory inventory = new Inventory();
inventory = JsonSerializer.Deserialize<Inventory>(players.Inventory);
I'm getting this error:
System.Text.Json.JsonException: 'The JSON value could not be converted to Inventory. Path: $ | LineNumber: 0 | BytePositionInLine: 1.'
How can I read this correctly?
EDIT: testing with stackoverflow answers:
Main code:
List<Item> items = new List<Item>();
items = JsonSerializer.Deserialize<List<Item>>(players.Inventory);
Item class:
class Item {
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("slot")]
public int Slot { get; set; }
[JsonProperty("info")]
public string Info { get; set; }
[JsonProperty("type")]
public string Type { get; set; }
[JsonProperty("amount")]
public int Amount { get; set; }
}
Result: It's not throwing exception anymore but reading only 0 and nill
you will have to fix your json, one of the property info has value string, another has array. you have to select something one, null for example
var jsonParsed = JArray.Parse(json);
foreach (var item in jsonParsed)
{
if (((JObject)item)["info"].GetType().Name == "JArray"
|| ((JObject)item)["info"].GetType().Name == "JValue")
((JObject)item)["info"] = null;
}
List<Item> items = jsonParsed.ToObject<List<Item>>();
result
[
{
"name": "lc_cash",
"slot": 1,
"info": null,
"type": "item",
"amount": 591
},
{
"name": "advancedlockpick",
"slot": 2,
"info": null,
"type": "item",
"amount": 19
}
]
UPDATE
if you sometimes have
"info":{"food":30,"thirst":30}
it is better to change info property to this
[JsonProperty("info")]
public Dictionary<string,int> Info { get; set; }
or a little more complicated
[JsonProperty("info")]
public Info Info { get; set; }
public class Info
{
[JsonProperty("food")]
public int Food {get; set;}
[JsonProperty("thirst")]
public int Thirst {get; set;}
}
as long as the information here is right, you gave us a json array string.
the inventory is an object with an array, so the simple solution will be this:
Inventory inventory = new Inventory();
inventory.Item = JsonSerializer.Deserialize<List<Item>>(players.Inventory);
important to mention that since Item is a list, you should use plural name
I'm trying to deserialize a nested json-file that looks like the following sample:
The output i want is "flat" table. I'm using .net 4.0 and I do not have the option of using third-party librabies like json.net.
{
"responsetime": 33,
"products": {
"totalProducts": 25,
"products": [
{
"id": 1,
"name": "Bike One",
"colors": [
{
"colorId": 44,
"name": "green",
"chemicals": [
{
"chemicalId": 99,
"chemicalName": "abc"
},
{
"chemicalId": 45,
"chemicalName": "bcd"
}
]
},
{
"colorId": 42,
"name": "blue",
"chemicals": [
{
"chemicalId": 96,
"chemicalName": "def"
},
{
"chemicalId": 22,
"chemicalName": "lkj"
}
]
}
]
}
]
}
}
From this I have genereated the following classes:
public class ResponseObject
{
public int responsetime { get; set; }
public Products products { get; set; }
}
public class Products
{
public int totalProducts { get; set; }
public Product[] products { get; set; }
}
public class Product
{
public int id { get; set; }
public string name { get; set; }
public Color[] colors { get; set; }
}
public class Color
{
public int colorId { get; set; }
public string name { get; set; }
public Chemical[] chemicals { get; set; }
}
public class Chemical
{
public int chemicalId { get; set; }
public string chemicalName { get; set; }
}
The output i want is a flat structure like the following:
1 Bike One 44 green 99 abc
1 Bike One 44 green 45 bcd
1 Bike One 42 blue 96 def
1 Bike One 42 blue 22 lkj
I am able to obtain this with the following code, however this implies 3 foreach-loops which i'm afraid will give bad performance if there are i.e. 100.000 products with N colors and N chemicals each.
Is there any other way to flatten this that will perform better using "vanilla" .net?
String jsonFileContent = File.ReadAllText(#"C:\example.json");
JavaScriptSerializer js = new JavaScriptSerializer();
js.MaxJsonLength = Int32.MaxValue;
ResponseObject rspns = js.Deserialize<ResponseObject>(jsonFileContent);
foreach (var product in rspns.products.products)
{
foreach (var color in product.colors)
{
foreach (var chemical in color.chemicals)
{
Console.WriteLine(product.id);
Console.WriteLine(product.name);
Console.WriteLine(color.colorId);
Console.WriteLine(color.name);
Console.WriteLine(chemical.chemicalId);
Console.WriteLine(chemical.chemicalName);
}
}
}
You want to start having the ToString done by the same objects:
public class Products
{
public int totalProducts { get; set; }
public Product[] products { get; set; }
public override string ToString(){
// this iterates all products and stacks their string representation
var productsStrings = products.Select(x => x.ToString());
return productsStrings.Aggregate("", (a, n) => a + n + "\n").trimEnd();
}
}
public class Product
{
public int id { get; set; }
public string name { get; set; }
public Color[] colors { get; set; }
public override string ToString(){
// this gets all color strings and prepends the product string
// "1 Bike" + product (repeated CO times)
var colorSubstrings = colors.Select(x => x.GetSubstrings());
var appendOnStrings = colorSubstrings.Select(x => $"{id} {name} {x}");
return appendOnStrings.Aggregate("", (a, n) => a + n + "\n").trimEnd();
}
}
public class Color
{
public int colorId { get; set; }
public string name { get; set; }
public Chemical[] chemicals { get; set; }
public string[] GetSubstrings(){
// this gets all chemicals strings and prepends the color string
// "44 green" + chemicalString (repeated CH times)
return chemicals.Aggregate("", (a, chemical) => a + $"{colorId} {name} {chemical.ToString()} \n").trimEnd();
}
}
public class Chemical
{
public int chemicalId { get; set; }
public string chemicalName { get; set; }
public override string ToString(){
// this produces a string like -> "99 abc"
return $"{chemicalId} {chemicalName}";
}
}
Then you just parse the whole chain and call it:
String jsonFileContent = File.ReadAllText(#"C:\example.json");
JavaScriptSerializer js = new JavaScriptSerializer();
js.MaxJsonLength = Int32.MaxValue;
// deserializing JSON to rspns
ResponseObject rspns = js.Deserialize<ResponseObject>(jsonFileContent);
// result will contain the table with all products, colors and chemicals
var result = rspns.products.ToString();
// printing only once will surely improve the performances
Console.WriteLine(result);
I am trying to deserialise JSON files that have been created by a third party tool.
The files contain a property that can look like the below:
"RECIPE": [{
"ctPoint_0": {
"endTemperature": 25,
"hours": 0.07999999821186066,
"startTemperature": 25
},
"ctPoint_1": {
"endTemperature": 30,
"hours": 0.07999999821186066,
"startTemperature": 25
},
"ctPoint_2": {
"endTemperature": 25,
"hours": 0.07999999821186066,
"startTemperature": 30
},
"pointCount": 3,
"type": "CyclicTemp"
}, {
"cycles": 2,
"type": "Repeat"
}, {
"cycles": 1,
"duration": {
"days": 0,
"hours": 0
},
}]
I am using system.text.json to deserialise:
newProgram = JsonSerializer.Deserialize<Program>(jsonString, options);
Extract of my Program class:
public class Program
{
public IList<Recipe> RECIPE { get; set; }
}
Recipe and cyclic_data struct:
public struct Cyclic_Data
{
public float endTemperature { get; set; }
public float hours { get; set; }
public float startTemperature { get; set; }
}
public struct Recipe
{
public int cycles { get; set; }
// Would like to use this
public IList<Cyclic_Data> ctPoints { get; set; }
// this deserialises ok but obviously only to hardcoded limit
public Cyclic_Data ctPoint_0 { get; set; }
public Cyclic_Data ctPoint_1 { get; set; }
public Cyclic_Data ctPoint_2 { get; set; }
public Cyclic_Data ctPoint_3 { get; set; }
public Cyclic_Data ctPoint_4 { get; set; }
public Cyclic_Data ctPoint_5 { get; set; }
public Cyclic_Data ctPoint_6 { get; set; }
public Cyclic_Data ctPoint_7 { get; set; }
public Cyclic_Data ctPoint_8 { get; set; }
public Cyclic_Data ctPoint_9 { get; set; }
public Cyclic_Data ctPoint_10 { get; set; }
public Duration duration { get; set;}
public string type { get; set; }
public float temperature { get; set; }
public int pointCount { get; set; }
}
As per the comments, if I have a number of discrete variables of type Cyclic_Data e.g. ctPoint_0 then this successfully deserialises, however as this list could theoretically be arbitrarily large it would be a nonsense to try to declare all possible property names.
I would really like to use an IList to read in all ctPoint_X values but am struggling to find a way to do so. I was looking at the newton soft implementation instead and wondering whether [JsonProperty("name")] could be used with RegEx to solve this but could not find any successful example done in this way.
How can I deserialise this in a sensible manner?
EDIT:
I am currently looking at a custom JsonNamingPolicy to rename any property name matching a RegEx "^ctPoint_[0-9]+$" to ctPoints, will let you know if this succeeds, please comment if this is doomed to fail or if there is a better way..
EDIT 2:
I tried the method outlined above but it didn't work as the correct JSON for a list of items doesn't have the name at beginning of each item, however this started me thinking about the problem differently. What I ended up doing was some simple string replacements before the deserialisation. This worked fine :)
int location;
location = newText.IndexOf("\"ctPoint_0\":");
newText = newText.Replace("\"ctPoint_0\":", "\"ctPoints\": [");
if (location > 0)
{ int lastloc;
for (int i = 1; i < 999999; i++)
{
string nextString = "\"ctPoint_" + i.ToString() + "\": ";
lastloc = location;
location = newText.IndexOf(nextString);
newText = newText.Replace(nextString, "");
if (location == -1)
{
location = newText.IndexOf("}", lastloc);
newText = newText.Insert(location+1, "]");
break;
}
}
}
Thanks
You can try Dictionary<string, object> and check the object type during runtime
Here are the models required for this scenario
public class CtPoint
{
public int endTemperature { get; set; }
public double hours { get; set; }
public int startTemperature { get; set; }
}
public class Duration
{
public int days { get; set; }
public int hours { get; set; }
}
Here is the sample code to Deserialize the JSON using System.Text.Json.JsonSerializer
var results = System.Text.Json.JsonSerializer.Deserialize<List<Dictionary<string,object>>>(json);
foreach (var model in results)
{
foreach(var item in model)
{
if (item.Key.Contains("ctPoint"))
{
var ctPoint = System.Text.Json.JsonSerializer.Deserialize<CtPoint>(item.Value.ToString());
Console.WriteLine($"{item.Key}- {ctPoint.hours} {ctPoint.startTemperature} {ctPoint.endTemperature}");
}
else if (item.Key.Contains("duration"))
{
var duration = System.Text.Json.JsonSerializer.Deserialize<Duration>(item.Value.ToString());
Console.WriteLine($"{item.Key}- {duration.days} {duration.hours}");
}
else
{
Console.WriteLine($"{item.Key}- {item.Value.ToString()}");
}
}
}
Output
ctPoint_0- 0,0799999982118607 25 25
ctPoint_1- 0,0799999982118607 25 30
ctPoint_2- 0,0799999982118607 30 25
pointCount- 3
type- CyclicTemp
cycles- 2
type- Repeat
cycles- 1
duration- 0 0
I have the following JSON data containing multiple sub array, Any suggestion how can Deserialize this and print on my page.
NEXT STEP
: After this, I need to insert this data in SQL using bulk copy features.
C# Code
collect mydata= new JavaScriptSerializer().Deserialize<collect >(json);
foreach (var item in mydata.results)
{
context.Response.Write(item.newPrice + item.pName);
}
public class collect
{
public List<collection1> results { get; set; }
}
public class collection1
{
public List<data> collection1 { get; set; }
}
public class data
{
public string newPrice { get; set; }
public string pName { get; set; }
}
JSON Array :
{
"name": "Test 1",
"count": 3,
"version": 2,
"lastsuccess": "Thu Oct 09 2014 05:42:17 GMT+0000 (UTC)",
"results": {
"collection1": [
{
"newPrice": "12787",
"pName": "Sony Xperia M Dual Black"
},
{
"newPrice": "24999",
"pName": "LG Google Nexus 5 16 GB (Black)"
}
]
}
}
To answer your question regarding how to de serialize the JSON here is a solution... I am not sure what you mean by "Print on my page" as your question does not put that into any context however...
I used http://json2csharp.com to create the poco classes below...
public class Collection1
{
public string newPrice { get; set; }
public string pName { get; set; }
}
public class Results
{
public List<Collection1> collection1 { get; set; }
}
public class RootObject
{
public string name { get; set; }
public int count { get; set; }
public int version { get; set; }
public string lastsuccess { get; set; }
public Results results { get; set; }
}
Then the following code will deserialize the JSON to C#...
RootObject myData = new JavaScriptSerializer().Deserialize<RootObject>(json);
You can now do with it as you please, in terms of your bulk insert... thats another question really so please start a new one.