As in the topic, I'm making a request to an endpoint, which in return gives me a json string. Sample json string (picked up 6 substrings, there is about thousand more):
{"probability":0.0062596053,"tagId":"sometagid","tagName":"apple","boundingBox":{"left":0.27482307,"top":0.4361664,"width":0.14311266,"height":0.37521422}},
{"probability":0.0061301645,"tagId":"sometagid","tagName":"apple","boundingBox":{"left":0.0,"top":0.44423538,"width":0.09239961,"height":0.37426883}},
{"probability":0.0059485333,"tagId":"sometagid","tagName":"carrot","boundingBox":{"left":0.037714787,"top":0.0,"width":0.15685204,"height":0.27176687}},
{"probability":0.005887271,"tagId":"sometagid","tagName":"tomato","boundingBox":{"left":0.5249929,"top":0.70379305,"width":0.44499594,"height":0.29620594}},
{"probability":0.0057223,"tagId":"sometagid","tagName":"apple","boundingBox":{"left":0.79498,"top":0.34279144,"width":0.19351125,"height":0.39170527}},
{"probability":0.0056102676,"tagId":"sometagid","tagName":"apple","boundingBox":{"left":0.030394234,"top":0.21933028,"width":0.16375154,"height":0.3037323}},
What do I need? I need this string to be splitted into these 6 ( 1000) objects (preferably to an array) and I want to pick only these object that contain probability*100 > 50
.
I've already made a class that contains such values as:
public class ResponseJsonNode {
public double probability { get; set; }
public string tagId { get; set; }
public string tagName { get; set; }
public BoundingBox boundingBox { get; set; }
}
And BoundingBox is another class:
public class BoundingBox {
double left { get; set; }
double top { get; set; }
double width { get; set; }
double height { get; set; }
}
Reproducible example (well not quite really because i can't post endpoint and key here):
using System.Net;
using System.Text.Json;
using ConsoleApp1;
WebRequest request = HttpWebRequest.Create("SomeUriEndpoint");
request.Method = "POST";
request.Headers.Add("some key", "some more key");
request.Headers.Add("some content type", "some more content type");
var f = File.Open(args[0], FileMode.Open);
using (var ms = new MemoryStream()) {
f.CopyTo(ms);
var fileBytes = ms.ToArray();
request.ContentLength = fileBytes.Length;
Stream stream = request.GetRequestStream();
stream.Write(fileBytes, 0, fileBytes.Length);
stream.Close();
//imageStringBase64 = Convert.ToBase64String(fileBytes);
}
HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result;
string json = new StreamReader(response.GetResponseStream()).ReadToEnd();
//JsonObject jo = (JsonObject)json;
List<ResponseJsonNode> jsonNodeList = JsonSerializer.Deserialize<List<ResponseJsonNode>>(json);
foreach(ResponseJsonNode rj in jsonNodeList) {
Console.WriteLine(rj);
}
And this gives me an error:
The JSON value could not be converted to System.Collections.Generic.List
This does not work also:
HttpWebResponse response = (HttpWebResponse)request.GetResponseAsync().Result;
string json = new StreamReader(response.GetResponseStream()).ReadToEnd();
//JsonObject jo = (JsonObject)json;
//List<ResponseJsonNode> jsonNodeList = JsonSerializer.Deserialize<List<ResponseJsonNode>>(json);
JsonArray jsonArray = JsonNode.Parse(json).AsArray();
List<ResponseJsonNode> nodes = new List<ResponseJsonNode>();
foreach(JsonObject jo in jsonArray) {
nodes.Add(new ResponseJsonNode { probability = Convert.ToDouble(jo["probability"]), tagName = (string)jo["tagName"] });
}
var stats = new Dictionary<string, double>();
foreach (ResponseJsonNode rjn in nodes) {
if (rjn.probability * 100 > 50)
if (stats.ContainsKey(rjn.tagName)) {
stats[rjn.tagName] ;
} else {
stats[rjn.tagName] = 1;
}
}
Throws an error: System.InvalidOperationException: The node must be of type 'JsonArray'
I have tried to parse it with numerous tutorials but every one of them seems deprecated or does not work (example shown above). So what is the best possible solution for converting json string into a iterable JsonObject? (Not specificly JsonObject class that is in c# libraries but something that i could iterate on)
CodePudding user response:
[Updated from clarification in this comment above.]
The JSON you're showing isn't an array. Which is why you can't deserialize it into an array. It's an object which contains an array. But in order to access that array you need to deserialize the object.
So deserialize the object. For example, using this class:
public class ResponseObject
{
public IEnumerable<ResponseJsonNode> predictions { get; set; }
}
You can deserialize your object into that class:
ResponseJsonNode jsonNode = JsonSerializer.Deserialize<ResponseObject>(json);
Basically the problem you're running into is understanding the difference between an object and an array of objects, or an object and a property on an object. You need to understand your data structure(s) in order to use that data.
CodePudding user response:
If your json is literally you shown, you need to modify it a little bit, then deserialize in a standard way.
public class ResponseJsonNode {
public double Probability { get; set; }
public string TagId { get; set; }
public string TagName { get; set; }
public BoundingBox BoundingBox { get; set; }
public override string ToString() =>
$"[Node]: Probability: {Probability}; TagId: {TagId}; TagName: {TagName};\nBoundingBox: {BoundingBox}";
}
public class BoundingBox
{
public double Left { get; set; }
public double Top { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public override string ToString() => $"[L:{Left};T:{Top};Width:{Width};Height:{Height}]";
}
Deserialize then:
var json = GetJsonFromApi();
var trimmedJson = $"[{json.TrimEnd(',')}]";
var collection = JsonSerializer.Deserialize<List<ResponseJsonNode>>(trimmedJson, new JsonSerializerOptions(JsonSerializerDefaults.Web));
foreach (var item in collection)
{
Console.WriteLine($"{item}\n");
}
Btw, C# naming convention recommends name properties in a PascalCase, for deserialization from camelCase just use JsonSerializerDefaults.Web
options.
and I want to pick only these object that contain probability*100 > 50.
Then simple LINQ filtration comes to the scene.