JSON to DataTable net with varing roots - json

This is my first dealing with JSON and unfortunately I have to do it in VB.Net, company policy. So I have searched high and low and tried a lot of examples even converted some of the C# code to VB.Net but I can't seem to find a complete solution.
I receive a JSON string like this:
{
"65080007":{
"partNo":"ATD000007",
"description":"Swingarm Hat Platform",
"quantity":4,
"assemblyseq":""
},
"65080143":{
"partNo":"ATD000143",
"description":"ASY Gas Spring Bracket",
"quantity":2,
"assemblyseq":""
},
"65080071":{
"partNo":"ATD000071",
"description":"TT Gas Spring",
"quantity":2,
"assemblyseq":""
},
"65080147":{
"partNo":"ATD000147",
"description":"ASY Lateral Hinge",
"quantity":8,
"assemblyseq":""
},
"65085181":{
"partNo":"RD0181",
"description":"ASY KIT Bolt, Carriage, 0.375 in x 16, 1.5 in (x45) & Nut, Flange, 0.375 in x 16 (x45)",
"quantity":1,
"assemblyseq":""
},
"65080796":{
"partNo":"ATD000796",
"description":"Decal, TT Equipped, Rectangular, 5 in x 10 in",
"quantity":1,
"assemblyseq":""
},
"65080797":{
"partNo":"ATD000797",
"description":"Decal, TT Open/Close, Triangular, 12 in x 8 in",
"quantity":1,
"assemblyseq":""
},
"65080745":{
"partNo":"ATD000745",
"description":"",
"quantity":1,
"assemblyseq":""
}
}
What I need to do bind or assign this data to a DataGridView.DataSource.
I've seen some examples but I can't set it as a DataSource.
I've tried this example:
Sub Main()
Dim json_result = GetJson()
Dim table = JsonConvert.DeserializeAnonymousType(json_result, (DataTable))
Dim newJString = Newtonsoft.Json.JsonConvert.SerializeObject(table, Newtonsoft.Json.Formatting.Indented)
Console.WriteLine("Re-serialized JSON: ")
Console.WriteLine(newJString)
Console.WriteLine("")
End Sub
Public Function GetJson() As String
Dim json_result As String = <![CDATA[
' I used the above json string
Return json_result
End Function
I have made my JSON classes to deserialise the JSON and tried JsonConvert.Deserialize keep getting an error it epected a array and found an object.
Public Class Jobs
'<JsonProperty("partno")>
Public Property PartNo As String
' <JsonProperty("description")>
Public Property Description As String
'<JsonProperty("quantity")>
Public Property Quantity As String
'<JsonProperty("assemblyseq")>
Public Property Assemblyseq As String
End Class
The issue I think is the root properties "65080797" these numbers will not be the same every time we get the JSON back from NetSuite.
So I tried to:
Dim obj = JsonConvert.DeserializeObject(Of Jobs)(result)
Console.WriteLine(obj.PartNo) it comes out PartNo = nothing
So I've tried this:
Dim resultA = JsonUtil.Deserialize(Of Jobs)(result, ignoreRoot:=True)
Module JsonUtil
Function Deserialize(Of T As Class)(ByVal json As String, ByVal ignoreRoot As Boolean) As T
Return If(ignoreRoot, JObject.Parse(json)?.Properties()?.First()?.Value?.ToObject(Of T)(), JObject.Parse(json)?.ToObject(Of T)())
End Function
End Module
This gives me the first group:
ATD000007
Swingarm Hat Platform
4
The assembly number was blank.
I'm open for any suggestions on how I can get the above JSON into a data table or DataGridView or how to make a list without the root "65080797" this number will be unique with every response.
The people that designed this response string refuses to remove the root properties.
Thank you for taking the time to read this mess.
All comments/suggestions are appreciated.

Yes, Json array looks like [something] instead of {somthing}, you could convert it to array if you want but you can also do it in different ways and even without any external library, you could create a datatable then bind it to datagridview or you could add data directly to datagridview.
Anyway, I made a datatable for you.
Imports System.Web.Script.Serialization 'for reading of JSON (+add the reference to System.Web.Extensions library)
Dim JSONC = New JavaScriptSerializer().Deserialize(Of Dictionary(Of String, Dictionary(Of String, String)))(JSON) 'you could do it in different ways too
Dim NewDT As New DataTable
'Create Columns
For Each key In JSONC.First.Value.Keys
'"65080007" <first
'{ "partNo":"ATD000007", "description":"Swingarm Hat Platform", "quantity":4, "assemblyseq":"" } <first.value
' "partNo" <first.value.key(s) :"ATD000007" <first.value.value(s)
NewDT.Columns.Add(key)
Next
'Add Rows
For Each item In JSONC
NewDT.Rows.Add(item.Value.Values.ToArray)
Next
DataGridView1.DataSource = NewDT

If you have unusually structured JSON like that, your best bet is probably to resort to using raw JObject and JToken handling instead of trying to get JSON.NET to deserialize into structured data automatically.
Create a method that will convert an individual JToken into the Job class you described in your question. I've added a Key property to identify those unique keys in the JSON that were problematic for you; if you don't need these, just remove the .Key bit from the example below.
Public Function CreateJob(data As JToken)
Return New Job With
{
.Key = data.Path,
.PartNo = data("partNo"),
.Description = data("description"),
.Quantity = data("quantity"),
.Assemblyseq = data("assemblyseq")
}
End Function
Once you have Job objects being created, you can walk the entire structure and turn it into an array of jobs with the sample code below.
Dim result As JObject = JsonConvert.DeserializeObject(json)
Dim jobs As Job() = result.Values().Select(Of Job)(AddressOf CreateJob).ToArray()
Once you have an array of Job objects, binding to a DataSource should be trivial.

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.

Set property of a nested JSON object

I'm new to JSON.Net and haven't been able to figure out how to get a nested object of my JSON as a JObject. I am using a file to store my JSON objects, and then write the changes I made back to the file.
Background
I am trying to make an app where the user selects a folder on their computer, and the app enumerates that folder and all of its files into a JSON object. The user can then select any of those files and set values of that file to be saved to the JSON file. So far I have made the function to generate the directory structure/contained files into JSON and save it to a file, but I am stuck at trying to then access a nested object of the JSON in order to set a property's value.
My code so far
The code used to generate the JSON and store it into the file is:
Private Sub btnAddFolder_Click(sender As Object, e As EventArgs) Handles btnAddFolder.Click
'Create JObject from JSON File
Dim json = File.ReadAllText(jsonFile)
Dim obj As New JObject
If json <> "" Then
obj = JObject.Parse(json)
End If
'Add directory as a nested object of JSON
' - Add all of files in directory as nested objects of the directory object
If obj.Property(txtDirectory.Text) Is Nothing Then
'Object to store all files as nested objects
Dim files As New JObject
'Object to store all properties of a file object
Dim fileInfo As New JObject
'Variables to store properties for fileInfo
Dim stringProp As String = "default"
Dim arrProp() As String = {"default"}
Dim jArrProp As JArray = JArray.FromObject(arrProp)
'Add properties to objcet
fileInfo.Add("var1", stringProp)
fileInfo.Add("var2", jArrProp)
'Add object of properties to each file object
For Each file As String In Directory.GetFiles(txtDirectory.Text)
files.Add(file, fileInfo)
Next
'Add each file object to directory object
obj.Add(txtDirectory.Text, files)
End If
'Write JSON to file
File.WriteAllText(jsonFile, JsonConvert.SerializeObject(obj, 1))
End Sub
The generated file/JSON looks like:
{
"Directory1": {
"File1": {
"var1": "default",
"var2": ["default"]
},
"File2": {
"var1": "default",
"var2": ["default"]
}
},
"Directory2": {
"File1": {
"var1": "default",
"var2": ["default"]
},
"File2": {
"var1": "default",
"var2": ["default"]
}
}
}
What I need next
The code above is called when a Button labeled Add Folder is clicked.
Another Button, labeled Select File and Set Data, prompts Users to select any file from the directory and input values that will be stored in var1 and var2 of that file.
For example, when the Select File and Set Data Button is clicked, a User selects File1, then writes "This file is a picture from my 2012 vacation to the islands." in a TextBox. I need my code to set var1 of obj.Directory1.File1 to the string value specified.
This is where I am stumped. I do not know how to use JSON.net to access a nested object in order to set a value of one of its properties.
I know that a JObject has a .Property().Value() method that can be used to set or get the value of a property, but since my intended JSON object is nested inside the main JSON object, I cannot figure out how to get it as a JObject in order to access that method. JObject.Item() returns a JToken which does not have a .Property().Value() method.
Your JSON structure contains Directories names or full paths, which are all different, so a class model is not really useful to describe the whole content of this JSON.
Since a Directory path is unique (you cannot have two identical paths that refer to distinct Directory roots), the Directory path can be used as the Key of a Dictionary.
It's also simple to determine whether the Dictionary already contains that Key (since you're checking this in your code), using the Dictionary.ContainsKey("Key") method.
The same applies to the Files names: usually a Directory cannot contain files with identical names. So you can use the File Path as the Key of a second Dictionary, which is the Value of the first one (which, as described, has the Directory as Key).
The var1 and var2 values are instead always the same, so you can use a class model to describe this structure. You can also assign default values to the properties (in different ways; here I'm just assigning a default value explicitly)
Public Class FileObj
<JsonProperty("var1")>
Public Property Var1 As String = "default"
<JsonProperty("var2")>
Public Property Var2 As List(Of String) = New List(Of String)({"default"})
End Class
At this point, when you have deserialize your JSON, you just check whether the Dictionary already contains a Key equal to the new Directory path.
If it does not, you add this new Key and a Dictionary(Of String, FileObj) as Value, where the Key is the File Name and FileObj is the class that contains your two properties:
Load the already saved content, if any:
Private DirectoryObjects As Dictionary(Of String, Dictionary(Of String, FileObj))
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim json = File.ReadAllText(jsonFile)
DirectoryObjects = JsonConvert.DeserializeObject(
Of Dictionary(Of String, Dictionary(Of String, FileObj)))(json)
End Sub
Add a new Directory structure to the current Dictionary:
Private Sub btnAddFolder_Click(sender As Object, e As EventArgs) Handles btnAddFolder.Click
Dim dirPath = txtDirectory.Text
If DirectoryObjects.ContainsKey(dirPath) Then Return
Dim fileInfo As New Dictionary(Of String, FileObj)()
For Each file As String In Directory.GetFiles(dirPath)
fileInfo.Add(file, New FileObj() With {
.Var1 = "Something",
.Var2 = New List(Of String)({"Something", "Else"})
})
Next
DirectoryObjects.Add(dirPath, fileInfo)
' Now you can serialize and write to disc to preserve the changes
Dim dirInfoJson = JsonConvert.SerializeObject(DirectoryObjects)
File.WriteAllText(jsonFile, dirInfoJson)
End Sub
Now, you can use, e.g., a ComboBox - to select one of the Directories - and a ListBox / ListView - to show the content of the selected Directory.
When one File is selected, the Directory is the Key of the outer Dictionary, the File the Key of the inner Dictionary.
► A Dictionary.ToList() can be used as the DataSource of most Controls that hold collection of items.
txtDescription.Text is the meant to represent the User input related to var1
txtDataVar2a.Text and txtDataVar2b.Text other inputs that represent the values of var2.
Private Sub btnEditFileInfo_Click(sender As Object, e As EventArgs) Handles btnEditFileInfo.Click
' Get the inner Dictionary using the Directory as the Key of the outer Dictionary
Dim fileObjects As Dictionary(Of String, FileObj) = DirectoryObjects(txtDirectory.Text)
' Get the FileObj object using the File as the Key of the inner Dictionary
Dim fileInfo As FileObj = fileObjects("[Selected File Path]")
' Add the new Values to var1 and var2
fileInfo.Var1 = txtDescription.Text
fileInfo.Var2 = New List(Of String)({txtDataVar2a.Text, txtDataVar2b.Text})
' You can serialize the Dictionary now or do it later
Dim dirInfoJson = JsonConvert.SerializeObject(DirectoryObjects)
File.WriteAllText(jsonFile, dirInfoJson)
End Sub
You'll see that this JSON structure is identical to the one you're showing here.
Note:
There's the chance that File names in new File Systems are case sensitive, but this is not a problem because our string comparison is also case sensitive.

Using vb.net to parse through json code with exception

I am trying to use vb.net windows forms application to parse through some JSON code. My code builds just fine and compiles, but the moment I click on my button, I get this exception:
Newtonsoft.Json.JsonReaderException: 'Error parsing NaN value. Path '', line 1, position 1.'
The exception is pointing to this line at the end of my code:
For Each item As JObject In JArray.Parse(jsonObjects.SelectTokens("items").ToString)
I have tried to solve it, but I don't know how to anymore. Here is my code:
Imports Newtonsoft.Json
Imports Newtonsoft.Json.Linq
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim jsonString As String = "{""id"":1, ""name"":""tom"",
""items"":[
{""name"":""apple"", ""count"":1 },
{""name"":""milk"", ""count"":2 }
]
}"
Dim jsonObjects As JObject = JObject.Parse(jsonString)
Console.WriteLine("id:" & jsonObjects.SelectTokens("id").ToString)
Console.WriteLine("name:" & jsonObjects.SelectTokens("name").ToString)
Console.WriteLine("items:" & jsonObjects.SelectTokens("items").ToString)
'Dim jsonArray As JArray = JArray.Parse(jsonObjects.SelectTokens("items").ToString)
For Each item As JObject In JArray.Parse(jsonObjects.SelectTokens("items").ToString)
Console.WriteLine("item:" & item.SelectTokens("name").ToString)
Next
End Sub
End Class
I am trying to ... parse through some JSON
Before you get to the exception at the loop start, this wont result in anything very useful:
Console.WriteLine("id:" & jsonObjects.SelectTokens("id").ToString)
SelectTokens returns an IEnumerable(Of Newtonsoft.Json.Linq.JToken). Trying to display it with ToString will just result in:
Newtonsoft.Json.Linq.JsonPath.FieldFilter+<ExecuteFilter>d__0
If you want several items from throughout the JSON it would probably be easier to deserialize it to a NET object or collection, but thats not what you asked. This will list the items that Tom has:
Dim jobj = JObject.Parse(Candyjson)
' print the Id, name and how many items there are
Console.WriteLine("Id: {0} Name: {1} has {2} Items", jobj("id"), jobj("name"),
jobj("items").Count)
' print each item name and how many there are
For n As Int32 = 0 To jobj("items").Count - 1
Console.WriteLine("Item: {0}, Count: {1}",
jobj("items")(n)("name"),
jobj("items")(n)("count")
)
Next
Results:
Id: 1 Name: tom has 2 Items
Item: apple, Count: 1
Item: milk, Count: 2
One you parse the JSON, the resulting JObject will contains various other things depending on what is in the JSON. If you set a breakpoint and examine jobj, the result will look much the same as your JSON (string), but it is in a form you can query:

What is wrong with this 1 line code?

dim json2=Newtonsoft.Json.Linq.JObject.Parse("[{""currency_code"":""1ST"",""cash"":""0"",""reserved"":""0""},{""currency_code"":""8BT"",""cash"":""0"",""reserved"":""0""}]")
The doublequotes are just escape characters. The actual string is
[{"currency_code":"1ST","cash":"0","reserved":"0"},{"currency_code":"8BT","cash":"0","reserved":"0"}]
Which as you can see is a legitimate json.
Basically it looks like a legitimate json array. I want to turn that into an array of strings
Basically I want to turn that into
{
"{""currency_code"":""1ST"",""cash"":""0"",""reserved"":""0""}",
"{""currency_code"":""8BT"",""cash"":""0"",""reserved"":""0""}"
}
First I parse that into a jobject andd............ I failed.
It's a legitimate json. Why I can't parse that into a jobject?
Some background.
I created 2 json helper function to help me parse any json. One convert json to dictionary. Another parse json into array. In fact, the whole json, is either dictionary (nested or not nested) or array (nested or not nested). Am I correct with this one?
Now the codes are:
Public Shared Function jObjectToDictionary(json As String) As Generic.Dictionary(Of String, String)
Dim json1 = JObject.Parse(json)
Dim dict = json1.ToObject(Of Generic.Dictionary(Of String, Object))
Dim dict2 = dict.ToDictionary(Function(x) x.Key, Function(x) x.Value.ToString)
Return dict2
End Function
Public Shared Function jObjectToArray(json As String) As Generic.List(Of String)
Dim json2 = JObject.Parse(json)
Dim ar = json2.ToObject(Of Generic.List(Of Object))
Dim ar2 = ar.ConvertAll(Function(x) x.ToString)
Return ar2
End Function
The code sort of works.
I then did
Dim jsonar = jsonHelper.jObjectToDictionary(_jsonResult)(starttoken) 'works
Dim ar1 = jsonHelper.jObjectToArray(jsonar) 'doesn't work
Then I examine why jObjectToArray doesn't work and it leads to this one line code that I think should work but doesn't.
That's what I ask. If you can fix this problem of that one line code, I can figure the rest out.
The reason that JObject.Parse is failing is because your JSON represents an array of objects, not just a single object. You can tell because the JSON begins and ends with square brackets [ and ], as opposed to curly braces { and } which denote a single object. To parse an array, you need to use JArray.Parse. (Note you can also use JToken.Parse -- that will parse either a single object or an array.)
Dim json2 = Newtonsoft.Json.Linq.JArray.Parse("[{""currency_code"":""1ST"",""cash"":""0"",""reserved"":""0""},{""currency_code"":""8BT"",""cash"":""0"",""reserved"":""0""}]")
Demo fiddle: https://dotnetfiddle.net/hZlc3m

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