Deserialize JSON string in to multiple C# objects - c#

I have a JSON string in below format for which I want to deserialize it into C# List. But the record number "1","2","3" (it can be upto 1,2,3...n depends on the json response each time) in JSON restricting me to deserialize it into C# object using Newtonsoft.Json
{
"1":{
"UID":"1",
"LICENCENO":"licenseno",
"NAME":"ABC"
},
"2":{
"UID":"2",
"LICENCENO":"licenseno",
"NAME":"PQR"
},
"3":{
"UID":"3",
"LICENCENO":"licenseno",
"NAME":"XYZ"
}
}
I am using below code for deserialization
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<List<DriverMaster>>(json);
I have DriverMaster class created as-
public class DriverMaster
{
public string UID { get; set; }
public string LICENCENO { get; set; }
public string NAME { get; set; }
}
Deserialization line giving unhandled exception, I know I am doing it in wrong way, because DriverMaster json object cannot be extracted into c# directly without doing something to record number 1,2,3...n in c#. Can anyone please help me to sort it out? Thanks in advance.

You were close:
var result = JsonConvert.DeserializeObject<Dictionary<string, DriverMaster>>(json)
.Select(x => x.Value)
.ToList();

Solution.
Change your code to use...
var result = JsonConvert.DeserializeObject<Dictionary<int, DriverMaster>>(json);
Explaination
The type is not the same... The List<DriverMaster>type will convert to JSON like so...
{
"1":
{
"DriverMaster": {
"UID":"1",
"LICENCENO":"licenseno",
"NAME":"ABC"
}
}
}
This doesn't match what you showed in your question...
The type that you are looking for is actually Dictionary<int, DriverMaster>, which is a key/value pair which will output a JSON string like so
{
"1": { ... },
"2": { ... },
"3": { ... }
}
In order to fix that, you need to use the Dictionary<int, DriverMaster> type instead.

For these types of things I like to use the often overlooked feature of JToken.SelectTokens. This function allows you to select tokens within a json string and permits the use of wildcards.
Here's some code that will deserialize your sample by selecting past the 1,2,3...N in the json:
public static IEnumerable<DriverMaster> Deserialize(string json)
{
return JToken.Parse(json).SelectTokens("*")
.Select(jToken => jToken.ToObject<DriverMaster>());
}
The * basically says to select all tokens after the root, so it's selecting the values associated with 1, 2, 3.. etc... Here's another SO answer that shows a more complicated usage of the SelectTokens method.

You need to use
public class DriverMaster
{
public string UID { get; set; }
public string LICENCENO { get; set; }
public string NAME { get; set; }
}
public class Root
{
[JsonExtensionData]
public IDictionary<string,JToken> Data {get;set;}
}
and
var result = Newtonsoft.Json.JsonConvert.DeserializeObject<Root>(json);
If you want to have result as List, you can parse the result as.
var list = new List<DriverMaster>();
foreach(KeyValuePair<string, JToken> token in result.Data)
{
list.Add(token.Value.ToObject<DriverMaster>());
}
That would give you the desired result as
1 licenseno ABC
2 licenseno PQR
3 licenseno XYZ

Related

Trying to get first object out of an json array in C#

I'm trying to get the first object out of a json array in c#. The Array looks something like this:
[
{
"name": "Joe",
"id": 1
},
{
"name": "Melinda"
"id": 2
}
]
I didn't find a suitable way to do this so I'm asking here. I'm using System.Text.JSON. I'm currently using this code:
class Program
{
public static void Main(String[] args)
{
HttpClient client = new HttpClient();
string url = "example.com";
string json = client.GetStringAsync(url).ToString()!;
Sensor sensor = JsonSerializer.Deserialize<Sensor>(json)!;
Console.WriteLine(sensor.id);
}
}
public class Sensor
{
public string? id { get; set; }
}
Now, unsurprisingly, when i run this code, System.Text.Json throws an error, but i cant decipher what exactly caused it (prbl bc im stupid):
inner exception System.Text.Json.JsonReaderException: 'S' is an invalid start of a value. LineNumber: 0 | BytePositionInLine: 0.
at System.Text.Json.ThrowHelper.ThrowJsonReaderException(Utf8JsonReader& json, ExceptionResource resource, Byte nextByte, ReadOnlySpan`1 bytes)
at System.Text.Json.Utf8JsonReader.ConsumeValue(Byte marker)
at System.Text.Json.Utf8JsonReader.ReadFirstToken(Byte first)
at System.Text.Json.Utf8JsonReader.ReadSingleSegment()
at System.Text.Json.Utf8JsonReader.Read()
at System.Text.Json.Serialization.JsonConverter`1.ReadCore(Utf8JsonReader& reader, JsonSerializerOptions options, ReadStack& state)
Is there an easy way to do this with System.Text.Json or Newtonsoft.Json?
Thx
You should deserialize json string as new List() and then you can find first element of the list using FirstOrDefault() method as follow :
class Sensor
{
public int Id { get; set; }
public string Name { get; set; }
}
public Sensor GetFirstElementOfJsonArray(String data)
{
JsonSerializerOptions options = new JsonSerializerOptions(){
PropertyNameCaseInsensitive = true };
List<Sensor> sensorList=JsonConvert.Deserialize<List<Sensor>>(data,options);
return sensorList.FirstOrDefault();
}
I think , it will the answer of your question
An approach very close to yours:
using System;
using System.Text.Json;
public class Program
{
public static readonly string data = #"[{""name"": ""Joe"",""id"": 1},{""name"": ""Melinda"", ""id"": 2 }]";
public static void Main()
{
// System.Text.Json defaults to case-sensitive property matching,
// so I need to switch this to insesitive, if the model adheres
// to C# naming convention ( Props start with capital letter)
JsonSerializerOptions jso = new JsonSerializerOptions(){ PropertyNameCaseInsensitive = true };
// We are deserializing an Array vv
var sensors = JsonSerializer.Deserialize<Sensor[]>(data, jso);
// I do an output for demonstration purposes.
// You'd want to check for null and size>0 and then use the first element.
foreach( var sensor in sensors )
{
Console.WriteLine($"{sensor.Id:#0} : {sensor.Name}");
}
}
}
public class Sensor
{
public int Id {get; set;}
public string Name {get; set;}
}
See in action: https://dotnetfiddle.net/t7Dkh8
Another Idea would be to incrementally parse, which is beneficial if the array is long and you only need the first element.
See Incremental JSON Parsing in C#
(Needs NewtonSoft, though)
Another remark that I and Jon Skeet already made in comments:
The errormessage you are getting
'S' is an invalid start of a value. LineNumber: 0 ...
hints towards that the received string might not actually be valid json. So you might want to investigate this, too.
You could set a breakpoint and look into the value using the debugger,
just spit it out to a text file or if you have logging, log it.
There are two issues that I see in your question. The first is that the id field in the json is not a string but an integer. So you either need to change your json so that it looks like this:
[
{
"name": "Joe",
"id": 1,
...
or update your Sensor class to look like this:
public class Sensor
{
public int id { get; set; }
public string? name { get; set; }
}
Once you do that though the other issue is that your json is not an object, but an array. so your code needs to look more like this:
HttpClient client = new HttpClient();
string url = "example.com";
string json = client.GetStringAsync(url).ToString()!;
var sensors = JsonSerializer.Deserialize<IEnumerable<Sensor>>(json)!;
Console.WriteLine(sensors.First().id);
So serialize the json into a collection (IEnumerable), then you can query that collection to get whatever data you need. Also, I don't know if that was just representative data, but in your json example above, there is a comma missing after "Melinda" in the json.
you need to deserialise to a class:
public class Sensor {
public int Id { get; set; }
public string Name { get; set; }
}
JsonSerializer.Deserialize<Sensor>(json)

Deserializing JSON with multi-level nesting

Yet another mtgjson.com inspired question; none of the other, similar questions are getting me where I need to be. First, a couple lines of sample JSON (from mtgjson's AllPrices.json):
"00028782-6ec2-54fe-8633-2c906d8f1076": {"prices": {"mtgo": {}, "mtgoFoil": {}, "paper": {"2019-12-01": 0.15}, "paperFoil": {}}},
"00040b50-3b84-5cea-b663-70038b87fa08": {"prices": {"mtgo": {"2019-12-02": 0.02}, "mtgoFoil": {"2019-12-02": 0.02}, "paper": {"2019-12-01": 0.15}, "paperFoil": {"2019-12-01": 0.53}}}
Each parent object is a GUID and the Price Info; the Price Info is the four types of prices offered, and for each of those four types, the price data is Last Updated Date and Price.
The classes I've created (after lots of other approaches, all of which have failed):
public class price_Class
{
public string Updated { get; set; }
public decimal Price { get; set; }
}
public class PriceInfo
{
[JsonProperty("mtgo")] public price_Class mtgo { get; set; }
[JsonProperty("mtgoFoil")] public price_Class mtgof { get; set; }
[JsonProperty("paper")] public price_Class RegPrice { get; set; }
[JsonProperty("paperFoil")] public price_Class FoilPrice { get; set; }
}
And how I'm using it:
dynamic prices = JsonConvert.DeserializeObject(sJSON);
IDictionary<string, JToken> pricelist = prices;
foreach (var priceline in pricelist)
{
sUUID = priceline.Key.ToString();
PriceInfo pi = JsonConvert.DeserializeObject<PriceInfo>(priceline.Value.ToString());
Stepping through in debug mode, I see that prices seems fine; pricelist, also. The foreach defines priceline as I'd expect, and sUUID is correctly defined - but pi shows up with all four sets of price data as null - not just those that are null, but those that should have data.
priceline.value looks fine, to me:
{{
"mtgo": {},
"mtgoFoil": {},
"paper": {
"2019-12-01": 0.53
},
"paperFoil": {
"2019-12-01": 4.53
}
}}
When I expand pi in the Locals window, it shows the four classes (FoilPrice, RegPrice, mtgo, mtgof), but the contents are null.
What I need, in case it's not obvious, is to have pi.RegPrice and pi.FoilPrice defined, with a Date and Price, when that data actually exists in the JSON.
I'll admit, nested classes and JSON in general is still outside my comfort zone; I appreciate all help!
The price_Class is not adequate for deserialize your JSON object.
try with this:
public class PriceInfo
{
[JsonProperty("mtgo")] public Dictionary<string, decimal> mtgo { get; set; }
[JsonProperty("mtgoFoil")] public Dictionary<string, decimal> mtgof { get; set; }
[JsonProperty("paper")] public Dictionary<string, decimal> RegPrice { get; set; }
[JsonProperty("paperFoil")] public Dictionary<string, decimal> FoilPrice { get; set; }
}
using this tool you can find exactly what are your DTO
the problem are on your json data as i believe date are not send like this in json it come as array of integers and with specific order like day month year .
as well as you need to create your DTO similar to json even in types you can't map it till you set the same type of json in your DTO
public class Prices
{
public Mtgo mtgo { get; set; }
public MtgoFoil mtgoFoil { get; set; }
public Paper paper { get; set; }
public PaperFoil paperFoil { get; set; }
}
public class RootObject
{
public Prices prices { get; set; }
}
don't forget to tag all of them with [JsonProperty("json prop name ")]
Parsing and formatting utilities for JSON.
A central concept in lift-json library is Json AST which models the structure of a JSON document as a syntax tree.
sealed abstract class JValue
case object JNothing extends JValue // 'zero' for JValue
case object JNull extends JValue
case class JString(s: String) extends JValue
case class JDouble(num: Double) extends JValue
case class JInt(num: BigInt) extends JValue
case class JBool(value: Boolean) extends JValue
case class JField(name: String, value: JValue) extends JValue
case class JObject(obj: List[JField]) extends JValue
case class JArray(arr: List[JValue]) extends JValue
It comes with Lift, but non-Lift users can add lift-json as a dependency in following ways. Note, replace XXX with correct Lift version.
SBT users
Add dependency to your project description:
val lift_json = "net.liftweb" %% "lift-json" % "XXX"
Maven users
Add dependency to your pom:
<dependency>
<groupId>net.liftweb</groupId>
<artifactId>lift-json</artifactId>
<version>XXX</version>
</dependency>
Summary of the features:
Fast JSON parser
LINQ style queries
Case classes can be used to extract values from parsed JSON
Diff & merge
DSL to produce valid JSON
XPath like expressions and HOFs to manipulate JSON
Pretty and compact printing
XML conversions
Serialization
Low level pull parser API
Try using this for deeply nested JSONs.
It seems that the odd sub-structure of {Prices:{label:{date:amount}}} just doesn't work well with Newtonsoft's (otherwise excellent) JSON tools.
I tried the various tools (some suggested here) to generate classes; they were getting confused by the dates, creating classes for each date. I even tried generating classes for just the substring of data (priceline.value, in the example) - nope, still wouldn't work.
I ended going with a brute-force, string manipulation approach; it's ugly, I'm not exactly proud of it - but I now have what I needed. Here's the relevant snippets, just in case anyone else stumbles on the same things as I did:
private static string RemoveNoise(string input)
{
input = Regex.Replace(input, #"\r\n?|\n", string.Empty); // no more NewLine stuff
return input.Replace(" ", string.Empty)
.Replace(#"""",string.Empty);
}
...
public class PriceData
{
public string UUID { get; set; }
public string Updated { get; set; }
public string Price { get; set; }
public string FoilUpd { get; set; }
public string FoilPrc { get; set; }
}
...
string sPaperTag = #"PAPER:{";
string sPprFlTag = #"PAPERFOIL:{";
...
dynamic prices = JsonConvert.DeserializeObject(sJSON);
IDictionary pricelist = prices;
foreach (var priceline in pricelist)
{
PriceData pData = new PriceData();
pData.UUID = priceline.Key.ToString();
bool bWeHavePrice = false;
string pi = RemoveNoise(priceline.Value.ToString().ToUpper());
// parse out paper, paperFoil dates & prices manually (unusual JSON format...)
iBeg = pi.IndexOf(sPaperTag);
if (iBeg >= 0)
{
sTemp = pi.Substring(iBeg, pi.Length - iBeg);
iBeg = sTemp.IndexOf(":") + 2;
iEnd = sTemp.IndexOf("}");
sTemp = sTemp.Substring(iBeg, iEnd - iBeg); // either YYYY-MM-DD:n.nn, or an empty string
iBeg = sTemp.IndexOf(":");
if (iBeg > 0)
{
if (DateTime.TryParse(sTemp.Substring(0, iBeg), out dtTemp)) { pData.Updated = dtTemp.ToString(); bWeHavePrice = true; }
if (Decimal.TryParse(sTemp.Substring(++iBeg, sTemp.Length - iBeg), out decTemp)) { pData.Price = decTemp.ToString(); bWeHavePrice = true; }
}
}
I do that string manipulation dance again for the foil prices; I'm not currently interested in the 'mtgo' or 'mtgoFoil' data.
I'm doing all that TryParse stuff to make sure I have a valid date or amount, but I'm using the results to populate parameters in a SQLCommand, so I have to have strings; seems like extra work, going from string to Date or Decimal, then back to string - but this way I don't get exceptions when executing the SQL Insert command.
My thanks to all who helped, or tried to help. And if someone figures out how to handle it via JSON.Net, I'd love to see it!

C# JsonConvert.DeserializeObject to list / add item to array

I'm currently trying to add a item to a array. But I think a list would be way easier since I could use
list.Add("whatever");
Is there a way to receive the following as a list?
dynamic reps = JsonConvert.DeserializeObject("rep.json");
Example json:
{"reps": {
{
"username": "usera",
"reps": 10,
"latestrep": "userx"
},
"userb": {
"username": "userb",
"reps": 10,
"latestrep": "userx"
},
"userc": {
"username": "userc",
"reps": 10,
"latestrep": "userx"
}
}}
I appreciate any kind of help
Providing you have a class defining the list item:
public class UserReps
{
public string Username { get; set; }
public int Reps { get; set; }
public string LatestRep { get; set; }
}
You can achieve what you want with LINQ:
IDictionary<string, IDictionary<string, UserReps>> parsed = JsonConvert.DeserializeObject<IDictionary<string, IDictionary<string, UserReps>>>(json);
List<UserReps> userReps = parsed["reps"].Select(ur => ur.Value).ToList();
I think you are looking for something like this:
`
public static dynamic FromJsonToDynamic(this object value)
{
dynamic result = value.ToString().FromJsonToDynamic();
return result;
}
`
That is an extension method that I use for generic objects. If you are looking at something to convert it to a particular object you could create an extension method like this:
public static T FromJson<T>(this object value) where T : class
{
T result = value.ToString().FromJson<T>();
return result;
}
With your data as it is at the moment, no. Your input data doesnt appear to be a collection or something that could logically be 'listified'.

How to serialize a List<object> to JSON set of properties (not JSON array)

I'd like to convert a List<some_object> to JSON.
public class some_object
{
public string field1 {get; set;}
public string field2 {get; set;}
}
I want to serialize this:
var somejson = new {
some_objects = new List<some_object>() {...some items...};
}
Standard serialization produces an array:
{
"some_objects": [
{
"field1":"value1",
"field2":"value2"
},
{
"field1":"value3",
"field2":"value4"
}
]
}
Instead, I want to produce something like this (more XML-like):
{
"some_objects":
{
"some_object": {"field1":"value1", "field2":"value2"},
"some_object": {"field1":"value3", "field2":"value4"}
}
}
Is there any way of producing this result?
No, JSON object properties need to be unique
All of the properties of your objects need to be different. Conceptually, XML represents an object, but the tags don't directly link to the concept of a property. The JSON way to do this would be what you posted:
{
"some_objects": [
{"field1":"value1", "field2":"value2"},
{"field1":"value3", "field2":"value4"}
]
}
Alternatively, use a dictionary giving items unique names
If you want to give each item a name that can then be used to access them, you can use a dictionary:
var dict = new {
some_objects = new Dictionary<string,some_object>(){
{ "a" , new some_object { field1="value1",field2="value2" } },
{ "b" , new some_object { field1="value3",field2="value4" } },
}
}
This would serialize into:
{
"some_objects":
{
"a": {"field1":"value1", "field2":"value2"},
"b": {"field1":"value3", "field2":"value4"}
}
}
no, because you will get objects with the same name, called: some_object
think... if you could do it, how you will refer to one of them, using some_objects.some_object how do you know which is?
It seems like you want to store some kind of metadata, specifically the type of object, where with XML you could use an attribute. You would have to do something like add a separate property:
public class some_object
{
public string typeText { get { return GetType().ToString(); } }
public string field1 { get; set; }
public string field2 { get; set; }
}
Which would theoretically generate JSON that looks something like:
{
"some_objects": [
{
"typeText":"MyNamespace.some_object",
"field1":"value1",
"field2":"value2"
},
{
"typeText":"MyNamespace.some_object",
"field1":"value3",
"field2":"value4"
}
]
}
Depending on your needs, you might want to encapsulate this metadata into its own class.

C# JSON Parse gives error 'System.ArgumentNullException' when parsing JSON array

I'm trying to parse this JSON:
{"error":[],"result":{"XETHXXBT":{"a":["0.023769","64","64.000"],"b":["0.023756","42","42.000"],"c":["0.023756","1.21518360"],"v":["74038.22109284","130984.00945101"],"p":["0.023689","0.006974"],"t":[1272,2332],"l":["0.023440","0.023440"],"h":["0.024091","0.024669"],"o":"0.024084"}}}
The piece of data I want to get is
"c":["0.023756","1.21518360"],
More to the point, the first value of "c". The code I am using is:
JObject jObject = JObject.Parse(json);
double balance = (double)jObject["c"];
What am I doing wrong? How can I just get the first value of the "c" array?
Thanks!
Here is the full error report: http://pastebin.com/4S204aUG
To get to c do the following:
var o = JObject.Parse(<yourjsontext>);
var result = o["result"];
var XETHXXBT = result["XETHXXBT"];
var c = XETHXXBT["c"];
var value1 = (double) c[0];
var value2 = (double) c[1];
Your problem is that your object is more complicated than it looks.
The answer is jObject['results']['XETHXXBT']['c'][0] or jObject['results']['XETHXXBT']['c'][1]. Take a look at it with a prettifier.
{
"error":[
],
"result":{
"XETHXXBT":{
"a":[
"0.023769",
"64",
"64.000"
],
"b":[
"0.023756",
"42",
"42.000"
],
"c":[
"0.023756",
"1.21518360"
],
"v":[
"74038.22109284",
"130984.00945101"
],
"p":[
"0.023689",
"0.006974"
],
"t":[
1272,
2332
],
"l":[
"0.023440",
"0.023440"
],
"h":[
"0.024091",
"0.024669"
],
"o":"0.024084"
}
}
}
As you can see, your base object does not have a c property, so naturally it is throwing an error when you attempt to access (and automatically type cast) that nonexistent property.
Use Like that it will give you more flexibility to get the data , if you don't know how may level depth you have to search it will become very difficult and code will be look like tightly coupled or hardcoded whenever you working with json or xml follow class structure it will help to debug or understand the structure as well
public class Test {
public string[] error { get; set; }
public string result { get; set; }
}
public class Result {
public List<Values> XETHXXBT { get; set; }
}
public class Values {
public List<Double> a { get; set; }
public List<Double> b { get; set; }
public List<Double> c { get; set; }
}
After that class Jsonconvert.deserializeObject<Test>(jsonString); and store it in object you will proper json data in object and after that you can traaverse and get desired data according to your need.Hope it will help you T
Thanks

Categories