Pains deserializing C# JSON string - c#

I am trying to post some data to my MVC 3 controller through a hidden text field that contains some JSON. I have that JSON passed in via string coursesList. Anyone have an idea why this is not working?
All I'm doing is making a byte [] out of the JSON string, writing it to a MemoryStream, and deserializing that stream -- or, attempting to. BookCourse bc always ends up with null properties.
Here's something like the JSON I would be using:
[{"coursesection":"1234","netlogon":"jsmith","label":"CRSE-1313 Generic Course Titling ~ Joe Smith"}]
And here's the object to be deserialized into:
using System.Runtime.Serialization;
namespace xxxx.Models
{
[DataContract]
public class BookCourse
{
[DataMember]
public string coursesection { get; set; }
[DataMember]
public string netlogon { get; set; }
[DataMember]
public string label { get; set; }
}
}
Finally, the controller action code to do it --
var byteArray = Encoding.ASCII.GetBytes(coursesList);
// Deserialize byte array to data type
var stream = new MemoryStream();
stream.Write(byteArray, 0, byteArray.Length);
var crs = new DataContractJsonSerializer(typeof(BookCourse));
stream.Position = 0;
// Read stream to object
ad.CourseSectionIDs = new List<int>();
try
{
var bc = (BookCourse) crs.ReadObject(stream);
while (bc.coursesection != null)
{
cs.AssociateCourseBook(bc.netlogon, bc.coursesection, ad.ISBN);
bc = (BookCourse)crs.ReadObject(stream);
}
}
catch (System.Runtime.Serialization.SerializationException e)
{
// Is this best practice for handling "none"?
}

Your JSON string represents a collection of BookCourse, not a single BookCourse. So adapt your code:
var serializer = new DataContractJsonSerializer(typeof(BookCourse[]));
and then:
var bookCourses = (BookCourse[])crs.ReadObject(stream);
or if you want to work with a single BookCourse you will need to change your JSON string and remove the wrapping square brackets which represent a collection.

Darin is correct, here is the change if you want to do it on a contract level.
[DataContract]
public class BookCourse
{
[DataMember]
public string coursesection { get; set; }
[DataMember]
public string netlogon { get; set; }
[DataMember]
public string label { get; set; }
}
[DataContract]
public class BookCourceCollection
{
[DataMember]
public List<BookCourse> Collection;
public static BookCourceCollection ReturnCollection(string jsonString)
{
MemoryStream ms;
BookCourceCollection collection;
using (ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString)))
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(BookCourceCollection));
collection = ser.ReadObject(ms) as BookCourceCollection;
}
return collection;
}
}
Usage:
string jsonString = "Your JSON string from the front end";
var bookCourceObject = BookCourseCollection.ReturnCollection(jsonString);
foreach (BookCourse bookCourse in bookCourceObject.Collection)
{
cs.AssociateCourseBook(bookCourse.netlogon, bookCourse.coursesection, bookCourse.ISBN);
}

Related

How do I convert json string into JsonObject and iterate on it? .NET Core 6.0 c#

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)
[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.
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.

How to deserialize specific element with DataContractJsonSerializer

I'm trying to deserialize json data with DataContractJsonSerializer class. a problem is how to set root element?
my json data is here.
{
"delete":{
"status":{
"id":696142765093072896,
"id_str":"696142765093072896",
"user_id":2223183576,
"user_id_str":"2223183576"
},
"timestamp_ms":"1454808363540"
}
}
and I wrote class for deserialization like this. but it isn't works. my Status always null.
[DataContract(Name="delete")]
public class Delete
{
[DataMember(Name="status")]
public DeletedStatus Status { get; set; }
}
public class DeletedStatus
{
[DataMember(Name = "id")]
public long Id { get; set; }
[DataMember(Name = "user_id")]
public long UserId { get; set; }
}
how can I start parse json from specific element?
Based on what I can tell from the JSON, the deserialization appears to be failing because the root property of the object is the "delete" property. I don't believe this will work with the DataContractJsonSerializer simply because the given type will not match the Delete type. One other possible issue is that I see the DeleteStatus class is missing a [DataContract] attribute.
Long story short, there is no simple way of doing what you want to do. That being said, there is a short and sweet way of deserializing the JSON without adding a lot of extra headache. I suggest creating a data type that represents the JSON in its current state, and deserialize to that type instead.
I wrote a Unit Test that you can run from a Visual Studio test project. I hope this helps.
JsonDeserializationTests.cs
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.Runtime.Serialization.Json;
using System.IO;
namespace SerializationTests {
[TestClass]
public class JsonDeserializationTests {
[TestMethod]
public void Deserialize_Delete_Type_Success() {
string json = string.Empty;
//Set the DataContractJsonSerializer target type to our wrapper type.
var ser = new DataContractJsonSerializer(typeof(DeleteWrapperJsonResult));
//Create an instance of the wrapper that reflects the JSON that you gave.
//This will help me mock the data that you gave.
var deleteWrapper = new DeleteWrapperJsonResult {
delete = new DeleteJsonResult {
status = new DeletedStatusJsonResult {
id = 696142765093072896,
user_id = 2223183576
}
}
};
//Convert the mock data to JSON to reflect the JSON that you gave.
using (var serStream = new MemoryStream()) {
using (var sr = new StreamReader(serStream)) {
ser.WriteObject(serStream, deleteWrapper);
serStream.Position = 0;
json = sr.ReadToEnd(); //Set the JSON string here.
//Output "{\"delete\":{\"status\":{\"id\":696142765093072896,\"id_str\":\"696142765093072896\",\"user_id\":2223183576,\"user_id_str\":\"2223183576\"}}}"
}
}
//Prepeare to Deserialize the JSON.
var deserialized = default(DeleteWrapperJsonResult);
using (var deserStream = new MemoryStream()) {
using (var sw = new StreamWriter(deserStream)) {
sw.Write(json); //Write the JSON to the MemoryStream
sw.Flush();
deserStream.Seek(0, SeekOrigin.Begin);
//Deserialize the JSON into an instance of our wrapper class.
//This works because of the structure of the JSON.
deserialized = (DeleteWrapperJsonResult)ser.ReadObject(deserStream);
}
}
//Initialize the actual Delete instanace with what was deserialized.
var delete = new Delete {
Status = new DeletedStatus {
//These values were populated with the JSON values.
UserId = deserialized.delete.status.user_id,
Id = deserialized.delete.status.id
}
};
//Write asserts around what was given and check for equality.
Assert.AreEqual(delete.Status.UserId, deleteWrapper.delete.status.user_id);
Assert.AreEqual(delete.Status.Id, deleteWrapper.delete.status.id);
//Test Passes for Me
}
}
}
Delete.cs
using System.Runtime.Serialization;
namespace SerializationTests {
[DataContract]
[KnownType(typeof(Delete))]
public class Delete {
[DataMember]
public DeletedStatus Status { get; set; }
}
[DataContract]
[KnownType(typeof(DeletedStatus))]
public class DeletedStatus {
[DataMember]
public long Id { get; set; }
[DataMember]
public long UserId { get; set; }
}
/**************************************************************
These types below are what comprise our wrapper class so that we can
use the JSON in its current state. The wrapper classes have properties that
are synonymous with the JSON properties.
**************************************************************/
//This structure represents the object nesting as it appears currently in your example.
[DataContract]
[KnownType(typeof(DeleteJsonResult))]
public class DeleteWrapperJsonResult {
[DataMember]
public DeleteJsonResult delete { get; set; }
}
[DataContract]
[KnownType(typeof(DeleteJsonResult))]
public class DeleteJsonResult {
[DataMember]
public DeletedStatusJsonResult status { get; set; }
}
[DataContract]
[KnownType(typeof(DeletedStatusJsonResult))]
public class DeletedStatusJsonResult {
[DataMember]
public long id { get; set; }
[DataMember]
public string id_str {
get {
return id.ToString();
}
set {
return;
}
}
[DataMember]
public long user_id { get; set; }
[DataMember]
public string user_id_str {
get {
return user_id.ToString();
}
set {
return;
}
}
}
}
As of the time of this writing, my unit test is passing! Let me know if I can assist further.

Deserialize a JSON stream

here's my class
[DataContract]
public class WytypowaneMecze
{
public WytypowaneMecze() { }
public WytypowaneMecze(String data, String d_gospodarzy, String d_gosci, String wynik)
{
this.Data = data;
this.D_gospodarzy = d_gospodarzy;
this.D_gosci = d_gosci;
this.Wynik = wynik;
}
public string Data { get; set; }
public string D_gospodarzy { get; set; }
public string D_gosci { get; set; }
public string Wynik { get; set; }
}
}
that's how i write to file my list wytypowane
private async void zapiszPlik()
{
string json = "wytypowane.json";
var serializer = new DataContractJsonSerializer(typeof(List<WytypowaneMecze>));
var stream = await Windows.ApplicationModel.Package.Current.InstalledLocation.OpenStreamForWriteAsync(json, CreationCollisionOption.OpenIfExists);
using (stream)
{
serializer.WriteObject(stream, wytypowane);
}
}
but i can't read this...
Additional information: '{}][{},{}][{}][{}][{},{}][{}][{}][{}][{}][{}][{}][{}]' is not a valid JSON primitive. This error can also occur when extraneous data is present after the JSON data.
private async void odczyt()
{
string json = "wytypowane.json";
List<WytypowaneMecze> lista = new List<WytypowaneMecze>();
var deserializer = new DataContractJsonSerializer(typeof(List<WytypowaneMecze>));
var stream = await Windows.ApplicationModel.Package.Current.InstalledLocation.OpenStreamForReadAsync(json);
using (stream)
{
lista = (List<WytypowaneMecze>)deserializer.ReadObject(stream);
}
}
You need to mark the properties you want to serialize with the DataMember attribute. That's because you are using the DataContractJsonSerializer and data contracts are opt-in:
Apply the DataMemberAttribute attribute in conjunction with the DataContractAttribute to identify members of a type that are part of a data contract. One of the serializers that can serialize data contracts is the DataContractSerializer.
The data contract model is an "opt-in" model. Applying the DataMemberAttribute to a field or property explicitly specifies that the member value will be serialized. In contrast, the BinaryFormatter serializes public and private fields of a type, and the XmlSerializer serializes only public fields and properties of a type.
Thus:
[DataContract]
public class WytypowaneMecze
{
public WytypowaneMecze() { }
public WytypowaneMecze(String data, String d_gospodarzy, String d_gosci, String wynik)
{
this.Data = data;
this.D_gospodarzy = d_gospodarzy;
this.D_gosci = d_gosci;
this.Wynik = wynik;
}
[DataMember]
public string Data { get; set; }
[DataMember]
public string D_gospodarzy { get; set; }
[DataMember]
public string D_gosci { get; set; }
[DataMember]
public string Wynik { get; set; }
}

Unable to serialize C# clas - get Unknown Type error and suggestion to use KnownType Attribute

I have the following class defined
[DataContract(Name="PatientDocument")]
[KnownType(typeof(PatientDocument))]
public class PatientDocument
{
public PatientDocument()
{
//
// TODO: Add constructor logic here
//
}
[DataMember]
public virtual String r_object_id { get; set; }
[DataMember]
public virtual int i_partition { get; set; }
[DataMember]
public virtual String nhs_consultant { get; set; }
.
.
.
.
[DataMember]
public virtual String batch_name { get; set; }
[DataMember]
public virtual String document_type_code { get; set; }
[DataMember]
public virtual String nhs_patdoc_singlebodysite { get; set; }
}
and this is used in the following function
PatientDocuments pds = null;
PatientDocument[] results = null;
String PatientId = String.Empty;
StreamReader sr = null;
DataContractJsonSerializer serializer = null;
try
{
using ( MemoryStream ms = new MemoryStream())
{
pds = new PatientDocuments(Properties.Settings.Default.DataConnection);
serializer = new DataContractJsonSerializer(typeof(PatientDocument));
PatientId = context.Request.QueryString["PatientId"];
results = pds.Select(PatientId);
serializer.WriteObject(ms,results);
ms.Position = 0;
sr = new StreamReader(ms);
// now write to the context/browser
context.Response.ContentType = "text/plain";
context.Response.Write(sr.ReadToEnd());
}
}
catch(System.Exception ex)
{
throw;
}
The line
serializer.WriteObject(ms,results);
is failing with the message
Type 'PatientDocument[]' with data contract name 'ArrayOfPatientDocument:http://schemas.datacontract.org/2004/07/' is not expected. Add any types not known statically to the list of known types - for example, by using the KnownTypeAttribute attribute or by adding them to the list of known types passed to DataContractSerializer.
I can't see how to use KnownAttribute to allow me to serialize the class.
Using .Net 3.5
You are trying to serialize an array of PatiantDocument objects using a serializer created for a single PatientDocument. Change your serializer initialization to:
serializer = new DataContractJsonSerializer(typeof(PatientDocument[]));
You don't need the KnownType attribute because you don't have any properties of non-primitive types (unless there are some in the snipped code).

Deserialize JSON string to a class

I have a very simple DataContract class that I need to deserialise into. I'm not using JSON.NET for a particular reason so use DataContractJsonSerializer.
My contract class looks like this
[DataContract(Name = "d")]
public class ResponseClass
{
[DataMember]
public int ResponseCode
{ get; set;}
[DataMember]
public string ResponseMessage
{ get; set;}
[DataMember]
public string Response
{ get; set;}
}
The service call is coming back with a valid Json string, but in deserialising, the value at the end is always 0 or string.Empty despite the value being passed in being non-0 or non string.Empty.
My deserialise code looks like this
private string deserializeJSON(string resp, bool code = false)
{
string value = string.Empty;
ResponseClass deserialized = null;
using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(resp)))
{
stream.Position = 0;
var ser = new DataContractJsonSerializer(typeof(ResponseClass));
deserialized = ser.ReadObject(stream) as ResponseClass;
}
value = code ? deserialized.Response : deserialized.ResponseCode.ToString();
return value;
}
I've looked around for an answer on this but nothing is springing back.
The data back looks like this (which is why the contract back is called "d" - not sure if that makes a difference or not, but the result is still the same, RespCode is 0, RespMess,Resp = string.Empty)
{
"d":
{
"RespCode": 0,
"RespMess": null,
"Resp": null
}
}
Since your C# class properties (e.g. ResponseCode) and JSON names (e.g. RespCode) don't match, you need to specify those Names in your DataMember attributes. And the structure with the d could be represented by another class (I made ResponseClassContainer for it), not by simply naming the DataContract d. This works:
private string deserializeJSON(string resp, bool code = false)
{
string value = string.Empty;
ResponseClass deserialized = null;
using (MemoryStream stream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(resp)))
{
stream.Position = 0;
var ser = new DataContractJsonSerializer(typeof(ResponseClassContainer));
deserialized = (ser.ReadObject(stream) as ResponseClassContainer).d;
}
value = code ? deserialized.Response : deserialized.ResponseCode.ToString();
return value;
}
[DataContract]
public class ResponseClassContainer
{
[DataMember]
public ResponseClass d { get; set; }
}
[DataContract]
public class ResponseClass
{
[DataMember(Name="RespCode")]
public int ResponseCode
{ get; set;}
[DataMember(Name="RespMess")]
public string ResponseMessage
{ get; set;}
[DataMember(Name="Resp")]
public string Response
{ get; set;}
}
The 0 integer and null strings were present because those are the default values for their respective types. Since the string didn't have any recognized data, those were all that were ever set there.

Categories

Resources