Trying to get first object out of an json array in C# - 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)

Related

How to pass JSON string as array

[
{"id": 1, "name": "danny_devito", "img": "/images/1"},
{"id": 2, "name": "jim_carey", "img": "/images/2"},
{"id": 3, "name": "tyler_1", "img": "/images/3"}
]
[System.Serializable]
public class Players {
public int id;
public string name;
public string img;
}
[System.Serializable]
public class PlayersArray {
public Players[] playersData;
}
string playersJson = File.ReadAllText(Application.dataPath + "/playersFile.json");
PlayersArray loadedPlayerData = (PlayersArray)JsonUtility.FromJson<PlayersArray>(playersJson);
Debug.Log("Danny Boy: " + loadedPlayerData.playersData[0].name);
I followed tons of tutorials and none of it works!
It gives me this error:
You're trying to deserialize an object. But the JSON you show isn't an object. It's an array. Deserialize it into a collection:
var loadedPlayerData = JsonUtility.FromJson<List<Players>>(playersJson);
Or even just an array:
var loadedPlayerData = JsonUtility.FromJson<Players[]>(playersJson);
As an aside... Names are important. Players is a misleading name for a player. The class should be called Player instead.
Additionally, this class might not deserialize at all, depending on how the JSON serializer works. They tend to use properties, not fields. You'll likely want to use properties anyway:
public class Players {
public int id { get; set; }
public string name { get; set; }
public string img { get; set; }
}
For further improvements, you will probably also want to capitalize the property names. The JSON serializer might be case-sensitive (I don't think they tend to be by default, but it's worth testing) and you may need to add attributes to the properties to specify their names in the JSON. But just to get the code working, at the very least you'll most likely need properties here instead of fields.

C# Why does System.Text.Json doesnt deserialise string

Given this model class:
using System.Text.Json;
public class QueueMessage
{
public int MerchantId { get; }
public string Message { get; }
}
When I try to deserialize a json string into the type QueueMessage, the fields are set to default. 0 and null.
This is how I've tried to deserialize it:
var jsonString = "{\"MerchantId\":2,\"Message\":\"Message 2\"}";
QueueMessage message = JsonSerializer.Deserialize<QueueMessage>(jsonString, new JsonSerializerOptions
{
PropertyNameCaseInsensitive = true
});
message.Message is null
message.MerchantId is 0
I'm using .Net 5 by the way.
What have I tried
Well i try to use my ol' buddy Newtonsoft.Json
dynamic message = JsonConvert.DeserializeObject(jsonString);
dynamic mercId = message.MerchantId.Value; //THIS gives the expected value of 2
However,
QueueMessage msg = JsonConvert.DeserializeObject<QueueMessage>(jsonString);
Gives the 0, null result
Now, the question is why does deserializing to a typed object fail?
As an alternative to Ziv's answer, and if you value the benefits of constructed types and/or immutability, recent versions of System.Text.Json now support \[JsonConstructor\], so you can now use constructed DTO types.
In my opinion you should always also specify an explicit [JsonPropertyName] to protect against DTO/JSON breakages caused by renamed properties or changing project-wide camelCase vs PascalCase JSON serialization settings.
It's also my opinion that JSON object members should always use camelCase, not PascalCase as that's the capitalization convention used by JavaScript/TypeScript, which is the usual target for JSON types (unfortunately STJ defaults to PascalCase (*grrrr*))
Example 1: With a simple constructor
...though this isn't a good example as it doesn't show the ctor actually doing anything useful.
using System.Text.Json;
public class QueueMessage
{
[JsonConstructor]
public QueueMessage(
int merchantId,
string? message
)
{
this.MerchantId = merchantId;
this.Message = message;
}
[JsonPropertyName("merchantId")] public int MerchantId { get; }
[JsonPropertyName("message") ] public string? Message { get; }
}
Example 2: With validating constructor
Because the constructor can make assertions and validate parameters it means the ctor can prevent QueueMessage.Message from ever being null, so the String? property can be changed to String which makes consuming the DTO nicer - and it can also validate MerchantId too:
using System.Text.Json;
public class QueueMessage
{
[JsonConstructor]
public QueueMessage(
int merchantId,
string message
)
{
this.MerchantId = merchantId > 0 ? merchantId : throw new ArgumentOutOfRangeException( paramName: nameof(merchantId), actualValue: merchantId, message: "Value must be positive and non-zero." );
this.Message = message ?? throw new ArgumentNullException(nameof(message));
}
/// <summary>Always >= 1.</summary>
[JsonPropertyName("merchantId")] public int MerchantId { get; }
/// <summary>Never null.</summary>
[JsonPropertyName("message") ] public string Message { get; }
}
Example 3: With record class
To keep things simpler, you could also just use a record class.
Though this loses the validation logic, so Message is now String? again.
Use the syntax [property: ] to apply JsonPropertyName and other attributes to record class properties via their ctor parameter names.
public record class QueueMessage(
[property: JsonPropertyName("merchantId")] int MerchantId,
[property: JsonPropertyName("message") ] string? Message
);
You need to add setters to your properties, otherwise the deserializer can't assign values to those properties
public class QueueMessage
{
public int MerchantId { get; set; }
public string Message { get; set; }
}

How can I deserialize this specific json string?

I am trying to deserialize the following json string using Newtonsoft Json. I am able to get the string successfully, but when I try using JsonConvert.DeserializeObject<ServerList>(response, settings);, the try catch fails.
[
{"endpoint":"127.0.0.1","id":6,"identifiers":["steam:","license:","xbl:","live:","discord:"],"name":"Blurr","ping":160},
{"endpoint":"127.0.0.1","id":7,"identifiers":["steam:","license:","xbl:","live:","discord:"],"name":"Knight","ping":120}
]
I believe my issue is because the players array being unnamed.
I have tried [JsonProperty("")] and [JsonProperty] for the Users var, I have also tried using List and Array instead of IList.
Here is the object. This may be completely wrong, I have tried many ways of doing this.
public class ServerList
{
// I have tried many ways of doing this array/list. This is just the latest way I tried.
[JsonProperty]
public static IList<Player> Users { get; set; }
}
public class Player
{
[JsonProperty("endpoint")]
public static string Endpoint { get; set; }
[JsonProperty("id")]
public static string ServerId { get; set; }
[JsonProperty("identifiers")]
public static IList<string> Identifiers { get; set; }
[JsonProperty("name")]
public static string Name { get; set; }
[JsonProperty("ping")]
public static int Ping { get; set; }
}
I am expecting to get a 'ServerList' object returned with a list of all the players connected to the server.
Ask any questions you need to, I don't often work with json in this format. Thank you in advance!
ERROR: Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.
Simplest way: Your json is an array, so deserialize to an array:
JsonConvert.DeserializeObject<Player[]>(response,settings);
As noted in the comments, properties on the Player should not be static.
If you insist on an object structure similar to what you posted, an object with a Usersproperty, even though it's not present in the JSON, that is also possible, by implementing a custom JSON converter. There's an article with an example of that here: https://www.jerriepelser.com/blog/custom-converters-in-json-net-case-study-1/
HOWEVER; I would recommend sticking to the types present in the json, and perhaps later construct the object that makes sense to the model in your program. This way, what you deserialize are true to the json you are getting, but make no sacrifices on the model you'd like:
var players = JsonConvert.DeserializeObject<Player[]>(response,settings);
var serverList = new ServerList {Users = players};

Converter that uses generics to parse json model

I have a converter class that receives json in input, here are 2 valid examples:
{
"method": "Model",
"payload": {
"key": "value"
}
}
and
{
"method": "OtherModel",
"payload": {
"foo": "bar"
}
}
In C#, I have classes mapped to each possible model:
public class Model
{
public string Key { get; set; }
}
public class OtherModel
{
public string Foo { get; set; }
}
I need a generic converter
How can I use the string value in the method of the JSON to convert in a generic way the content of the payload field?
Is using a huge switch the only way? This is the prototype I have so far but there are hundreds of different models so it will grow quite large...
public IResult ParseJson(string json)
{
Regex regexMessageName = new Regex("\"messageName\": \"(.*?)\"", RegexOptions.Compiled);
var messageName = regexMessageName.Match(json).Groups[1].Value;
switch (messageName)
{
case "Model":
var raw = JsonConvert.DeserializeObject<JsonData<Model>>(json);
return new LogInfoRequestResult<Model> { Raw = raw };
case "OtherModel":
var raw = JsonConvert.DeserializeObject<JsonData<OtherModel>>(json);
return new LogInfoRequestResult<OtherModel> { Raw = raw };
}
}
If you want complete control of your classes, and allow them to evolve independently, then you can have one base class that owns the Method, and then as many subclasses as you want with their own definition of the payload.
First, parse into the baseclass, just to get a strongly typed deserialization of Method
Then, there are a lot of patterns to address branching logic.
If you have 1-2 cases, an if statement is fine
If you have 3-5 cases, you can use a switch
If you have 6-10 cases, you can create a dictionary that maps method name to class type
If you have more than that, you can use the strategy pattern and pass an interface around
Here's an example of how you could write the code:
var json = #"{
'method': 'Model',
'payload': {
'key': 'value'
}
}";
var modelBase = JsonConvert.DeserializeObject<ModelBase>(json);
var methodMapping = new Dictionary<string, Type>()
{
{MethodTypes.Model.ToString(), typeof(Model)},
{MethodTypes.OtherModel.ToString(), typeof(OtherModel)},
};
Type methodClass = methodMapping[modelBase.Method];
var result = JsonConvert.DeserializeObject(json, methodClass);
Note: Since we're programmatically determining the correct type, it's hard to pass to a generic <T>, so this uses the overload of DeserializeObject that takes type as a param
And here are the classes that model incoming messages
public enum MethodTypes
{
Model,
OtherModel
}
public class ModelBase
{
public string Method { get; set; }
}
public class Model : ModelBase
{
public ModelInfo Payload { get; set; }
public class ModelInfo
{
public string Key { get; set; }
}
}
public class OtherModel : ModelBase
{
public ModelInfo Payload { get; set; }
public class ModelInfo
{
public string Foo { get; set; }
}
}
Dictionary<string,string>
If your data is always going to be "foo":"bar" or "key":"value" .... string:string, then Cid's suggesting to use Dictionary<string,string> Payload makes a lot of sense. Then figure out however you want to map from that c# class in a c# constructor that returns whatever type you want.
Additional Resources:
How to handle both a single item and an array for the same property using JSON.net
Deserializing polymorphic json classes without type information using json.net
JSON.NET - Conditional Type Deserialization
Conditionally deserialize JSON string or array property to C# object using JSON.NET?
You can instanciate an object of the expected class using Activator.CreateInstance(), then populate it with JsonConvert.PopulateObject()
In example :
Type t = Type.GetType($"NameSpaceName.{messageName}"); // this must be a fully qualified name
object obj = Activator.CreateInstance(t);
JsonConvert.PopulateObject(json, obj);

Deserialize JSON string in to multiple C# objects

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

Categories