VB.Net / JSON - Deserializing - json

I've been racking my brain for the past few hours.
I found that you can serialize the data and as such save the hassle of creating endlessly long .split commands for each of the seperate JSON values. So I've followed what a lot of people on SO and all over the web have done, by creating a JsonHelper class to Serialize and Deserialize the JSON that is passed from an ASP.net page.
This is my class:
Public Class NewTaskOb
Public Property Department As Integer
Public Property Type As Integer
Public Property AssignedTo As Integer
Public Property Priority As Integer
Public Property Title As String
Public Property JobCode As Integer
Public Property ParentTask As String
Public Property DueDate As Date
Public Property Scope As String
Public Property Comment As String
Public Property BudgetString As String
End Class
Then I have this:
<WebMethod()> Public Shared Function NewTaskRequest(task As String) As String
Dim newTask As NewTaskOb = DeserializeJSON.JsonDeserialize(Of NewTaskOb)(task)
My JSON is valid according to jsonlint, but when I pass the values through, I check by putting a breaker on the Dim newTask line, and when I hover my mouse over it, it says each of the values is empty (Department = 0, Type = 0, etc.) when, on the form, I am submitting values.
I have tried by using DataContractJsonSerializer, JsonHelper, and JavaScriptSerializer. Please note, I do not want to import another reference (i.e Newtonsoft.Json, or anything similar). I want to get this done using the aforementioned functions.
Thanks for your help in advance.
Edit (adding in the JSON string itself):
{
"NewTask": {
"Department": "-1",
"Type": "0",
"AssignedTo": "0",
"Priority": "-1",
"Title": "",
"JobCode": "-",
"ParentTask": "-",
"DueDate": "",
"Scope": "",
"Comment": "",
"BudgetString": ""
}
}
Edit:
While messing around with the code, I went back to the JavaScriptSerializer and found if I put Dim data = jss.Deserialize(Of Object)(task) in place of Dim data = jss.Deserialize(Of NewTaskOb)(task), then it shows up while stepping through that there is a dictionary in the data, that has the fields with the data.
Dictionary showing JSON correctly http://imageshack.com/a/img401/1174/a9ju.png
Any idea how I can extract these into the class variables?
Thanks in advance.

So I was testing your program on my system and noticed that it has Dictionary"2. Imagining what it could possibly mean, I had the idea that 2 could mean there is a dictionary nested within a dictionary - which could explain why you couldn't retrieve the values. So I edited away, and ended up with this line - Dim taskData = jss.Deserialize(Of Dictionary(Of Object, Object))(task). Now, when I run it I have put in Dim taskValues = taskData.Values, and I can reference each of the values using their respective index positions...
i.e taskValues(0) = Department, taskValues(1) = Type, etc.
Also, I removed the line where you were inserting the data into a Data Transfer Object, thus eliminating the "NewTask": nesting within the JSON, resulting in this JSON:
{
"Department": "-1",
"Type": "0",
"AssignedTo": "0",
"Priority": "-1",
"Title": "",
"JobCode": "-",
"ParentTask": "-",
"DueDate": "",
"Scope": "",
"Comment": "",
"BudgetString": ""
}

Related

How Do I Validate a JSON file With a Schema in VB.Net?

I'm trying to validate some JSON files on VB.net.
However, Whenever I run my code it gets stuck on
Dim Schema As JsonSchema = JsonSchema.Parse(SchemaString)
The Error Says
An unhandled exception of type 'Newtonsoft.Json.JsonException' occurred in Newtonsoft.Json.dll.
There is also a warning that says that JSON validation moved to its own package. So, I'm pretty sure I'm just importing the wrong packages, but I'm not sure.
I would be grateful if anyone could point me in the correct direction,
Thank you.
Here is my VB.net code
Imports System
Imports Newtonsoft.Json.Schema
Imports Newtonsoft.Json.Linq
Public Function Validate_JSON()
Dim SplunkPath As String = "Z:\Database Project\Splunk Folder\Dropbox\Splunk_File.json"
Dim SchemaPath As String = "Z:\Database Project\Schema Folder\Schema_File.json"
Dim Schema_String As String = My.Computer.FileSystem.ReadAllText(SchemaPath)
Dim Schema As JsonSchema = JsonSchema.Parse(Schema_String)
Dim Data_String As String = My.Computer.FileSystem.ReadAllText(SplunkPath)
Dim Data_JSON As JObject = JObject.Parse(Data_String)
Dim Splunk_Status As Boolean = Data_JSON.IsValid(Schema)
Return 0
End Function
Here is Splunk_File.json
{
"Site": "USI",
"SN": "21165",
"MN": "F2C00W",
"DateTime": "05/18/2021"
}
Here is Schema_File.json
{
"$schema" :"http://json-schema.org/draft-04/schema",
"properties": {
"$schema": "#",
"Site": {
"type": "string"
},
"SN": {
"Type": "string"
},
"MN": {
"Type": "string"
}
}
}
$schema is only valid at the root, and properties values MUST be schemas.
You have a "$schema" : "#" inside properties. This means that you're trying to say that your JSON object should have a property called schema that can validate against the schema #. But # isn't a valid schema object, so the parse fails.
You need to remove the $schema from your properties.
I'd also suggest using a later draft of the schema spec (if you have control over the schema). Draft 6 is the oldest version that's compatible with the latest, 2020-12.
But for this you'll likely need to use a different validation package. There are several available. Mine is JsonSchema.Net.

Unable to deserialize a JSON array with Json.Net

I have a JSON like this:
[
{
"areas": [
{
"area": "New York",
"isDay": true,
"temp": "14"
},
{
"area": "Washington DC",
"isDay": true,
"temp": "30"
},
{
"area": "Los Angles",
"isDay": false,
"temp": "54"
},
{
"area": "San Diego",
"isDay": true,
"temp": "59"
}
],
"status": true,
"code": 200,
"created_at": "2019/06/18 22:26:34.475",
"request_time": "2019/06/18 22:25:28.306"
}
]
I'm trying to get the value of the the area value on the first/second/third object under the Areas array.
I've tried this code to get the value of the first area of the array, which in this case is "New York".
'Note that 'rawJSON' is a string variable that contains the JSON.
Dim jResults As JObject = JObject.Parse(rawJSON)
Dim naming As String = jResults("areas")("name")
However when I run this, I get and exception:
Newtonsoft.Json.JsonReaderException: 'Error reading JObject from JsonReader. Current JsonReader item is not an object: StartArray. Path '', line 1, position 1.'
I am trying to write a function that imports all 'areas' in each array with their identifier of area (such as New York/Washington DC) into a ListBox (so the ListBox will contain, in order: New York, Washington DC, Los Angles).
How can I do this?
There are a couple of problems here:
Your JSON begins and ends with square brackets, so that means it is an array, not a single object. Since it is an array you need to use JArray.Parse() to parse it instead of JObject.Parse().
Inside your array, there is a single object which contains a property areas along with a few other properties like status and code. The value of the areas property is also an array, as denoted by square brackets. So your code needs to account for both of those arrays by adding the indexes. If you want the first item, that would be index zero.
There is no property called name anywhere in the JSON you posted. Instead, the names of the cities are each in a property called area.
Putting it all together, if you want to get the name New York from this JSON, you would do it like this:
Dim jResults As JArray = JArray.Parse(rawJSON)
Dim name As String = jResults(0)("areas")(0)("area")
Demo: https://dotnetfiddle.net/eAfEsx
If you're trying to get multiple names, then you would need a loop. You could do something like this:
Dim names As New List(Of String)()
For Each area As JObject In jResults(0)("areas")
Dim name As String = area("area")
names.Add(name)
Next
Demo: https://dotnetfiddle.net/BtSa6O
As you can see from the JSON structure you have posted, the JSON starts with a square bracket. That's the start of an array, the StartArray: when the JsonReader starts reading the JSON, the first TokenType is a JsonToken.StartArray (what the error is telling you).
Then there's an Object, defined by the braces: { }.
So you have an Array or List of Objects.
The first property of this object (areas) is of Type Array (or List).
It defines a collection of Objects.
Other properties, (status, code, etc.) have a single value.
[
{
"areas": [ { ... }, { ... } ]
"other": "other value"
}
]
You can parse the outer Array with JArray. The first element in the array (and the only one you're showing here), is the outer array. The first property of the first element in the outer array is the areas Array.
So you could write:
Dim areasArray = JArray.Parse(rawJson)(0)("areas")
Dim firstAreaValue = areasArray(0)("area").ToString()
The value of firstAreaValue is then "New York".
This is can be tiresome and prone to errors. You can generate a class Model from the JSON and use this model to deserialize the JSON to .Net classes, making it easier to handle, use as Source of Data, modify, serialize back etc.
Your JSON is an Array of Objects, so let's build the Root Object as a .Net class:
Note that I've set the Type of the CreatedAt and RequestTime properties to DateTime (Date): the string format of the DateTime, "2019/06/18 22:26:34.475", can be deserialized correctly.
DateTimeOffset is probably better. Your choice.
Public Class AreasRoot
<JsonProperty("areas")>
Public Property Areas As List(Of AreaObject)
<JsonProperty("status")>
Public Property Status As Boolean
<JsonProperty("code")>
Public Property Code As Long
<JsonProperty("created_at")>
Public Property CreatedAt As DateTime
<JsonProperty("request_time")>
Public Property RequestTime As DateTime
End Class
The Areas property is an Array or List of another type of object that contains 3 properties:
Public Class AreaObject
<JsonProperty("area")>
Public Property AreaArea As String
<JsonProperty("isDay")>
Public Property IsDay As Boolean
<JsonProperty("temp")>
Public Property Temp As Long
End Class
To deserialize using this Model, call JsonConvert.DeserializeObject(Of Type)(json).
The Type is, as mentioned, an Array or List(Of RootObject):
Dim areasArray = JsonConvert.DeserializeObject(Of List(Of AreasRoot))(rawJson).First()
Now New York is:
Dim firstArea = areasArray.Areas.First().Area
' Or
Dim firstArea = areasArray.Areas(0).Area
You can list all the Area names with:
For Each areaObj In areasArray.Areas
Console.WriteLine(areaObj.Area)
Next

Retrieve index of JSON array from

I have a standard JSON array returned to me which I have deserialized with JsonConvert, below being a simplified representative of my situation:
[
{
"name": "John",
"age": "21",
},
{
"name": "Sally",
"age": "18",
},
{
"name": "Harry",
"age": "25",
}
]
...
Public Class myExample
Public Property name as String
Public Property age as Integer
End Class
...
Dim serverResponse as string = reader.ReadToEnd()
Dim jsonResult = JsonConvert.DeserializeObject(Of List(Of myExample))(serverResponse)
I can easily retrieve the values of name or age given the item index, e.g.,
Dim someValue = jsonResult.Item(1).name ' returns Sally
Dim someOther = jsonResult.Item(1).age ' returns 18
Yet I wish to return the index (integer) of the array given the name: i.e., how might I search for Sally and retrieve the Item index integer (in this case 1), or in the case of John, return 0? I have not been able to achieve this with jsonResult.IndexOf(), or jsonResult.FindIndex() and searches have not been fruitful.
I have an extensive JSON and wish to loop through names on a dataGridViewer column, returning the ages of all names to another column.
Thanks for helping a novice out!
Since deserialization in this manner results in List(of T), the index to the above example can be found through, for example:
jsonResult.FindIndex((Function(s) s.name.Equals(someNameToCheck))))
or any other method of finding an item in a list.

obtain values to json file

I am making an application in vb net, and read JSON format files.
I know how, but I have a file that has a different format, you can see here:
{
"objects": {
"realms/lang/de_DE.lang": {
"hash": "729b2c09d5c588787b23127eeda2730f9c039194",
"size": 7784
},
"realms/lang/cy_GB.lang": {
"hash": "7b52463b2df4685d2d82c5d257fd5ec79843d618",
"size": 7688
},
"minecraft/sounds/mob/blaze/breathe4.ogg": {
"hash": "78d544a240d627005aaef6033fd646eafc66fe7a",
"size": 22054
},
"minecraft/sounds/dig/sand4.ogg": {
"hash": "37afa06f97d58767a1cd1382386db878be1532dd",
"size": 5491
}
}
}
It is different because all text has objects, no string, so I can not read it defined classes.
I just need the values of "hash", then add them to a textbox.
I hope you can help me.
Sounds like #Plutonix has seen this before, so I'll go with the Minecraft thing. It seems like objects is a Dictionary(Of String, MinecraftItem). A little tweak to the linked duplicate could be to create a class for the whole file, so you don't have to pull out the objects separately:
Public Class MinecraftData
Public Property Objects As Dictionary(Of String, MinecraftItem)
End Class
Public Class MinecraftItem
Public Property Hash As String
Public Property Size As Long
End Class
You can then parse the whole thing using Json.NET:
Dim data = JsonConvert.DeserializeObject(Of MinecraftData)(json)
If you are certain that you will only ever need the hashes, you can actually use a regex to get all the hashes from this file. Yes, this is slightly evil, because regex is normally not a suitable tool for dealing with structured data. But if you don't care about the structure...
Dim hashExtractor = new System.Text.RegularExpressions.Regex("[0-9a-f]{40}")
Dim matches = hashExtractor.Matches(myFileContents).Cast(of System.Text.RegularExpressions.Match)
Dim hashes = matches.Select(Function(m) m.Value).ToList()

Create dyanamic class to accept dynamic JSON

I have a web api endpoint that receives JSON and serializes it into objects. Very basic and common stuff. However, I have a requirement to accept custom user defined fields. For example, a developer may want to add a custom field for "account #" and pass that along via the API. I'm stuck how I could define a field on my class if I don't know the name. I need to support unlimited fields so I cannot simply create a field for custom1, custom2, custom2, etc.
I would think that my JSON could look something like this... where custom_label_xxx is identifies the field label:
...
"custom_fields": {
"custom_label_90": 49,
"custom_label_83": [ 28, 29, 30 ],
"custom_label_89": "2012/05/21"
},
...
How in the world can I setup a dynamic class to accept this dynamic JSON?
I have Googled forever and cannot find any examples using custom fields.
Your post method can accept dynamic as a param:
public HttpResponseMessage Post(dynamic obj)
This will accept every json that you send.
Saykor's reply is probably correct. However, I already have tons of business objects to work with and I only wanted the custom fields to be dynamic. Also, I wanted the Web API Help pages to be generated with sample JSON. So making the method dynamic would not work well for me.
What I finally did was probably obvious to some. I created a property on the class that was a dictionary. When I run the Help Pages it generates sample JSON that looks exactly what I needed. I have not tested this yet, but I assume it will serialize the JSON into a dictionary.
Here is the property in VB, but it could easily use dynamic in C# as well.
Private _customfields As Dictionary(Of String, String)
<DataMember(EmitDefaultValue:=False, IsRequired:=False, Order:=11)> _
Public Property customfields() As Dictionary(Of String, String)
Get
Return Me._customfields
End Get
Set(ByVal value As Dictionary(Of String, String))
Me._customfields = value
End Set
End Property
This resulted in the following JSON:
"customfields": {
"sample string 1": "sample string 2",
"sample string 3": "sample string 4",
"sample string 5": "sample string 6"
}