Unable to deserialize a JSON array with Json.Net - json

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

Related

How to add JObjects to a JArray when serializing a DataTable to JSON using custom formatting rules?

When attempting to serialize a DataTable to JSON using a custom format, I have some trouble adding JObjects (representing a row) to my JArray (representing the table).
So what I hope to make the table look like is something like this:
[
"Orderline" : {"Item": "Table", "Quantity": "5", "Amount": "50$"},
"Orderline" : {"Item": "Chair", "Quantity": "20", "Amount": "30$"},
"Orderline" : {"Item": "Couch", "Quantity": "2", "Amount": "500$"}
]
I have tried using the Add method to the JArray but I cannot seem to get it to work properly.
At the beginning I have set the JArray to new JArray, so that it is in memory and then I will go along and add the JObjects to it one at a time.
I can see that the add method takes two arguments (Item as JToken and content as object) which makes me a little confused because I do not see anyone else addressing both arguments in other code snippets I have seen online.
JArray is a instance of class Newtonsoft.Json.Linq.JArray.
Desired JObject output:
{
"Orderlines": {
"Item": "Table",
"Quantity": "5",
"Amount": "50"
}
}
Draft code:
Dim JsonObejct as JObject
Dim MyArray as Jarray
Dim Table as datatable
Set MyArray = new JArray
for each row in table
JsonObject = Jobject.FromObject(
New With {
Key.Orderlines = New With{
key.Item = row("Item").Tostring,
key.Quantity = row("Quantity").tostring,
key.Amount = row("Amount").tostring
}
}
)
Myarray.add(JsonObject)
Next row
I work in UiPath Studio and do not have the code in the same place its separated in multiple activities, so please don't get caught in wrongly defined details in the code, everything works until the add to jarray part.
I use Vb .net in UiPath Studio so I would appreciate a solution on how to add the JObects to my JArray in vb .net.
If you simply need to fix your compilation, the following compiles successfully and generates the required JSON structure:
Public Module DataTableJsonExtensions
Public Function ToSpecificOrderlines(ByVal table As DataTable) As JArray
Dim MyArray as JArray = New JArray
For Each row As DataRow In table.Rows
' Possibly you need to use CultureInfo.InvariantCulture in your ToString calls
Dim JsonObject As JObject = JObject.FromObject(
New With {
Key.Orderlines = New With{
key.Item = row("Item").ToString,
key.Quantity = row("Quantity").ToString,
key.Amount = row("Amount").ToString
}
}
)
MyArray.add(JsonObject)
Next row
Return MyArray
End Function
End Module
And then do
Dim myArray as JArray = DataTableJsonExtensions.ToSpecificOrderlines(table)
Demo fiddle #1 here.
However, it seems preferable to generalize the code to serialize any set of DataTable columns by name, with an optional specified format:
Public Module DataTableJsonExtensions
' Serialize all the columns of the table to an Orderlines array
Public Function ToOrderlines(ByVal table As DataTable) As JArray
Return ToOrderlines(table, table.Columns.Cast(Of DataColumn).Select(Function(c) c.ColumnName).ToArray())
End Function
' Serialize the selected columns of the table to an Orderlines array with the specified culture and formats (if any)
Public Function ToOrderlines(ByVal table As DataTable, ByVal columns() As String, Optional ByVal culture As CultureInfo = Nothing, Optional ByVal formats As IDictionary(Of String, String) = Nothing) As JArray
culture = If (IsNothing(culture), CultureInfo.InvariantCulture, culture)
Dim array as JArray = New JArray
For Each row As DataRow In table.Rows
Dim obj As JObject = new JObject (
new JProperty("Orderlines",
New JObject(columns.Select(Function(c) New JProperty(c, String.Format(culture, If(Not IsNothing(formats) AndAlso formats.ContainsKey(c), formats(c), "{0}"), row(c)))))
)
)
array.add(obj)
Next row
Return array
End Function
End Module
Then, to get the JSON shown at the top of your question, with the $ appended to the "Amount" property's value, do:
Dim formats = New Dictionary(Of String, String) From {{"Amount", "{0}$"}}
Dim myArray as JArray = DataTableJsonExtensions.ToOrderlines(table, {"Item", "Quantity", "Amount"}, Nothing, formats)
If you don't need the formatting after all, you call it as follows:
Dim myArray as JArray = DataTableJsonExtensions.ToOrderlines(table, {"Item", "Quantity", "Amount"})
Notes:
You can use DataTableJsonExtensions.ToOrderlines(table) to serialize all columns of the table to an Orderlines array.
In general serialization should always be performed using the invariant culture so that JSON serialized in one locale (say, the United States) can be deserialized in another locale (say, Europe) which uses a different decimal separator or DateTime format. ToString(), however, formats its values using the current culture. Thus I provided the option for you to specify a formatting culture in case you really want to serialize in CultureInfo.CurrentCulture. Passing Nothing for the culture results in using the invariant culture.
In the JSON shown in the top of your question you append a $ to the Amount column, so I provided an optional dictionary of formats in case you need to add that when serializing.
Demo fiddle #2 here.

VB Variable JSON String

In Visual Basic code, when I try to insert a variable into this JSON string it comes back
as a 400 bad request. How do I correctly feed this JSON string a variable?
Dim myJSON As String = "{""StoreId"":""12345"",""TerminalId"":""12345"",""CaptureMode"":""true"",""MerchantReferenceCode"":""VARIABLEINSERTEDHERE"",""InvoiceNumber"":""12345"",""TimeoutMinutes"":""5"",""ShowAddress"":""true"",""AddressRequired"":""true"",""TransactionTotal"":""33.33"",""TaxTotal"":""2.00"",""CustomerCode"":""12345""}"
Your JSON as it sits right now is an Object that looks like this:
{
"StoreId": "12345",
"TerminalId": "12345",
"CaptureMode": "true",
"MerchantReferenceCode": "VARIABLEINSERTEDHERE",
"InvoiceNumber": "12345",
"TimeoutMinutes": "5",
"ShowAddress": "true",
"AddressRequired": "true",
"TransactionTotal": "33.33",
"TaxTotal": "2.00",
"CustomerCode": "12345"
}
One option that you have is to create a new JObject, use the Add method (documentation) to build the object's properties, and then the ToString method (documentation) to serialize the object to JSON. This way you don't have to worry about properly formatting the JSON, just let the library do it for you.
Take a look at this example:
Dim myVariable = "VARIABLEINSTEREDHERE"
Dim request = New JObject()
request.Add("StoreId", 12345)
request.Add("TerminalId", 12345)
request.Add("CaptureMode", True)
request.Add("MerchantReferenceCode", myVariable)
request.Add("InvoiceNumber", 12345)
request.Add("TimeoutMinutes", 5)
request.Add("ShowAddress", True)
request.Add("AddressRequired", True)
request.Add("TransactionTotal", 33.33)
request.Add("TaxTotal", 2.00) ' taxation is theft
request.Add("CustomerCode", 12345)
Dim myJson = request.ToString()
Example: https://dotnetfiddle.net/FQDpVq
The short answer to the question you asked:
myjson = myjson.replace("VALUEPLACEHOLDER1","SoMeTHiNGeLSe")
Now, if you are talking about tricky pattern matching, then you will need to use regex if you want to approach this from a string-manipulation perspective. That sets you up for all sorts of unanticipated problems down the road with things you didn't anticipate, though, and you'll probably be happier in the long run if you embrace using JSON.
Try using the NewtonSoft JSON libraries. Then you can do something like:
dim myobj as new MyClass
myobj = jsonconvert.deserializeobject(of MyClass)(myjson)
That will turn your JSON string into an instance of MyClass called myobj, and you can access its properties directly, changing whatever you want:
myobj.MerchantReferenceCode = "SpecialMerchant"
Note that if the class definition for MyClass has properties that the JSON has no values for, they'll be present in the new class instance with no values. You can then set those values as you wish.
If the JSON has properties that don't exist in your class, they'll be dropped/lost in the conversion, so you do need to study your source data.
And you turn it back into a string again easily:
dim newjson as string = jsonconvert.serializeobject(myobj)

Better way to deserialize Minecraft json

I am trying to work with Minecraft json from (1.8.json download). A Sample:
{
"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
}
}
}
The actual json is much longer, some 2940 lines.
I need a way to deserialize this that isn't completely insane - using JSONUtils I get 4411 Lines of Code, but the same code can't be used for any other version of Minecraft.
The automated tools can be very useful, but they are not perfect - especially when it comes to dictionaries.
The first thing to note is that the structure is the same for all of them, that is they all consist of hash and size properties. This means rather than creating hundreds of identical classes we can use the same one over and over:
' the type that repeats from your robot:
Public Class MinecraftItem
Public Property hash As String
Public Property size As Int32
End Class
Since the automated tools are working on the fly (they do not look ahead...or back) they don't really know that they are all the same. The next thing is that the classes the robots generate wont work in this case:
Public Property minecraft/sounds/music/game/creative/creative3.ogg As _
MinecraftSoundsMusicGameCreativeCreative3Ogg
That is illegal as a property name. However, the names will work just fine as Dictionary keys. In addition to the above MinecraftItem class, we might want a container class:
Public Class MinecraftContainer
Public objects As Dictionary(Of String, MinecraftItem)
End Class
There are (at least) 3 ways to get at the data:
Method 1: Pluck out a single item
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Dim jstr As String = ...from whereever
' parse the json into a JObject
Dim js As JObject = JObject.Parse(jstr)
' if you somehow know the names, you can pluck out the data:
Dim mi = js("objects")("minecraft/sounds/mob/blaze/hit2.ogg")
Console.WriteLine(mi("hash").ToString & " " & mi("size").ToString)
The first line parses the original json string into a JObject. This allows us to work with the contents in various ways. For instance, we can reference "objects" in the json and them the items by name, which is exactly what happens in the next line:
' drill into "objects", get the "...hit2.ogg" item
Dim mi = js("objects")("minecraft/sounds/mob/blaze/hit2.ogg")
This will work if you just need to fetch a particular item from the big file. The mi variable created is a "special" json token, so use the names to get the data bits you want
hash = mi("hash").ToString
size = mi("size").ToString
Method 2: Deserialize To a Dictionary
This would be my preferred method. Include the same Import statements as in the first example, then:
' parse the json string
Dim js As JObject = JObject.Parse(jstr)
' deserialize the inner "objects" to a NET Dictionary
Dim myItems = JsonConvert.DeserializeObject(Of Dictionary(Of String, _
MinecraftItem))(js("objects").ToString)
This will create myItems as a Net Dictionary(Of String, MincraftItem) from the json. Since the MinecraftObject class doesn't do anything but hold the dictionary, we skipped it.
The keys are the long names and each value is a MinecraftItem which allows you to reference them more conventionally:
' get one of the items into a variable
gravel3 = myItems("minecraft/sounds/mob/chicken/step2.ogg")
ConsoleWriteLine("Gravel3 hash: {0}, size: {1}",
gravel3.hash, gravel3.size.ToString)
If you are not familiar with the .Net Dictionary it is somewhat like an array or List except you access your items by a Key rather then index. To loop thru them all:
Dim n As Integer = 0
For Each kvp As KeyValuePair(Of String, MinecraftItem) In myItems
Console.WriteLine("Name: {0} Hash: {1} size: {2}",
kvp.Key,
kvp.Value.hash,
kvp.Value.size.ToString)
n += 1
If n >= 2 Then Exit For ' just print 3
Next
Output:
Name: realms/lang/de_DE.lang Hash: 10a54fc66c8f479bb65c8d39c3b62265ac82e742 size: 8112
Name: realms/lang/cy_GB.lang Hash: 14cfb2f24e7d91dbc22a2a0e3b880d9829320243 size: 7347
Name: minecraft/sounds/mob/chicken/step2.ogg Hash: bf7fadaf64945f6b31c803d086ac6a652aabef9b size: 3838
Just remember, the Key will always be the long path name, and each .Value is MinecraftItem object.
Method 3: Deserialize To a Container
You can skip the parsing step by using the MinecraftContainer class:
Dim jstr As String = ...from whereever
Dim myJItems = JsonConvert.DeserializeObject(Of MinecraftContainer)(jstr)
Note that this time, you pass the entire string you downloaded to JsonConvert. The result will be an extra outer object which contains a dictionary property named "Objects". So, you reference the items using some leading references:
gravel3hash = myJItems.Object("minecraft/sounds/dig/gravel3.ogg").hash
gravel3 = myJItems.Object("minecraft/sounds/dig/gravel3.ogg")
ConsoleWriteLine("Gravel3 hash: {0}, size: {1}",
gravel3.hash, gravel3.size.ToString)
'or:
ConsoleWriteLine("Gravel3 hash: {0}, size: {1}",
myJItems.Object("minecraft/sounds/dig/gravel3.ogg").hash,
myJItems.Object("minecraft/sounds/dig/gravel3.ogg").size.ToString)
This method is just one line of code to deserialize, but it means
a) I have to define the container class and
b) I have to use myJItems.Object to drill into the otherwise empty container to get at the Dictionary.
Personally, I would forego this and use method 2 - one extra line of code makes it a bit easier to work with. That said, you can also extract the dictionary collection from the container:
Dim myItems As Dictionary(Of String, MinecraftItem)= myJItems.Object

VB.Net / JSON - Deserializing

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": ""
}

Making JSON object with Arrays

I'm looking to create what I feel is probably a basic JSON object, but my limited knowledge of JSON is making it difficult.
I am trying to create an object that will ultimately be passed to a .NET AMSX web service. The parameter for the web service is a P1Request object, which is defined as follows:
Public Class P1RequestClause
Public Property FieldId() As Integer
Public Property OperatorId() As Integer
Public Property Value() As String
End Class
Public Class P1Request
Public Property Fields() As String()
Public Property Clauses() As P1RequestClause()
End Class
On the client side, I have a number of different form fields, the values of which I would like to wrap up in a JSON object to pass.
I am unsure of what structure my JSON object needs to match the .NET class.
Ideally my data, in psudocode, would look like:
P1Request:
Fields:
Field1,
Field2,
Field3
Clauses:
P1RequestClause:
Id1,
OpId1,
SomeValue
P1RequestClause:
Id2,
Opid2,
AnotherValue
What would this look like in JSON? It's the array of Fields in P1Request is the part that confuses me the most. As I understand JSON, it's all name:value pairs, and making an array of a single field is throwing me.
{
"Fields": [
"moo",
"says",
"the cow"
],
"Clauses": [
{
"FieldId": 1,
"OperatorId": 3,
"Value": "foo"
},
{
"FieldId": 2,
"OperatorId": 0,
"Value": "bar"
}
]
}
JSON consists of primitive types (numbers, strings, null...), objects (which are key-value pair collections), and arrays, which is what you were missing.