Deserializing multiple JSON results - json

Dim js As New System.Web.Script.Serialization.JavaScriptSerializer
Dim json = js.Deserialize(Of jsonTest)(e.Result)
With json
'do stuff with object
End With
Private Class jsonTest
Public Artist As String
Public Album As String
Public Track As String
Public Duration As String
End Class
I managed to get this to work. However, I can't get it to work for multiple results.
Here's an example of my JSON output. How would I return multiple sets? "album" : "Move Along", "albumpos" : 1, "artist" : "The All American Rejects", "bitrate" : 128, "duration" : 195, "playing" : true, "position" : 101, "resolvedBy" : "DatabaseResolver", "score" : 1.0, "track" : "Track 01"

Ok, "multiple results" can mean a bunch of things. That's why nobody has given you a straight answer yet.
I THINK - you are asking about the idea of collections and sets on the server side. In other words, how do I return a json result set that looks like this (a json array):
[
{"Artist": "AMR", Album:"I Have No Idea", "Track": "Track 01", Duration: 195},
{"Artist": "AMR", Album:"I Have No Idea", "Track": "Track 02", Duration: 500},
{"Artist": "AMR", Album:"I Have No Idea", "Track": "Track 03", Duration: 400},
...
]
Then, If I'd REALLY like to return these various tracks grouped by album (or search phrase or some other arbitrary group), how do I return a result set that looks like this (an array of objects):
[
{"The All-American Rejects": [{"Artist":"AMR", ...}, {Artist:"AMR", ...}]}
{"Move Along": [{"Artist":"AMR", ...}, {Artist:"AMR", ...}]}
{"When the Work Comes Down": [{"Artist":"AMR", ...}, {Artist:"AMR", ...}]}
...
]
If that's NOT the intent of your question, then stop reading here. If it is, then you are looking for 2 objects in your VB code: a LIST and a DICTIONARY. Your class will hold the data for one and only one track. LIST and DICTIONARY are both standard collections within the language (a smarter sexier version of an array).
Let's rearrange, rename some of your code so that we use a List (and some names that make sense)...
Private Class Track
Public Property Artist As String
Public Property Album As String
Public Property Track As String
Public Property Duration As String
End Class
'CREATE MY LIST OF OBJECTS HERE
dim Album1 as new List(of Track)
Album1.Add(New Track() with {
.Artist = "AMR",
.Album = "The All American Rejects",
.Track = "Track 01",
.Duration = 910
})
Album1.Add(New Track() with {
... add track info again...
})
... add yet more track info ...
Dim js As New System.Web.Script.Serialization.JavaScriptSerializer
Dim json = js.Deserialize(Of List(of Track))(Album1)
... do whatever you'd like next ...
See how we messed with your Deserialize code?
js.Deserialize(Of List(of Track))(Album1)
Now, instead of deserializing 1 result, we are deserializing a LIST OF results. That code will give you the first result set (more or less). OK. Cool. So how do we return multiple albums? For that little trick, use a dictionary.
To create a list of tracks, the vb.net code is similar, but we use a dictionary AND a list:
'CREATE MY LIST OF OBJECTS HERE
dim results as new Dictionary(of string, of List(of Track))
dim Album1 as new List(of Track)
Album1.Add(New Track() with {
.Artist = "AMR",
.Album = "The All American Rejects",
.Track = "Track 01",
.Duration = 910
})
Album1.Add(New Track() with {
... add track info again...
})
... add yet more track info ...
results.Add("The All American Rejects", Album1)
... do the same thing with the next album ...
results.Add("MoveAlong", Album2)
Dim js As New System.Web.Script.Serialization.JavaScriptSerializer
Dim json = js.Deserialize(Of Dictionary(of string, List(of Track)))(results)
... do whatever you'd like next ...
That code will return that second set of json data that we outlined, like, 10,000 words ago.
IF this is the direction you wanted to head, then take a look at all the collections available at the server level, and maybe take a look at how to create properties from those collections. Those objects are where most of your "real" server code is going to live. And, if you code them correctly, serializing them (to json, to the database, to XML, etc) will be almost trivial!
Start by building on your Track class. Create an album class that looks like the one below and then populate it, serialize it and see what the result set looks like:
Public class Album
Public Property Name as String,
Public Property ReleaseDt as DateTime,
Public Property Tracks as List(of Track) = New List(of Track)
End Class
Hope that's what you were looking for. If it is, then stop right here. Go no further. Take one half-step backward and read a decent intro to Design Patterns. It'll get you used to working with Interfaces and objects and inheritance. And THAT will make any server-side code you write stunningly beautiful. Like ... drop dead sexy. Trust me. It's worth the time.
Good luck with the project.

Related

How to deserialize Json with fix named elements dynamically in vb.net?

I’m on the way to implement an interface to the Ameritrade Rest API in a vb.net application (with httpclient).
Amongst other things, I have to query quotes from a ticker list (e.g. AMD,MSFT,AMZN, ....).
The call of the API works without problems, I get a valid Json back, but the Json is not given back in a way, I would expect.
I now search the best way to handle that problem...
This is not the first interface to a Rest API, I have implemented.
Normally, I implement a corresponding data class in vb.net and then use JsonConvert (from Newtonsoft) to deserialize the Json string into my data class.
Example:
Dim oObject As New DataClass
oObject = JsonConvert.DeserializeObject(Of DataClass)(JsonString)
whereby DataClass is the vb.net class that is defined according to the data in the Json string.
Problem:
The ticker symbol-list to query is dynamic and can change from api call to api call.
If I - e.g. - query AMD and MSFT in a call, I get back (cut to only a few fields) the following Json:
{
"AMD": {
"assetType": "EQUITY",
"symbol": "AMD",
"description": "Advanced Micro Devices, Inc. - Common Stock",
"bidPrice": 92.11
},
"MSFT": {
"assetType": "EQUITY",
"symbol": "MSFT",
"description": "Microsoft Corporation - Common Stock",
"bidPrice": 243.1
}
}
To be able to deserialize the Json, I would have to implement the following DataClass:
Public Class DataClass
Public Property AMD As AMD
Public Property MSFT As MSFT
End Class
Public Class AMD
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
Public Class MSFT
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
This would work but is absolutely static and does not make any sense, as I would have to implement a (identical) class for any ticker, I maybe want to query in the feature.
I would expect to get back a dynamic list so that I could implement the class as following:
Public Class DataClass
Public Property TickerDetails As List(Of TickerDetail)
End Class
Public Class TickerDetail
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
This way, I would be able to deserialize in a List of TickerDetails and the go thru the list (no matter, which symbols I queried).
But, I can’t change, what I get back over the API...
Question:
What is the best way to handle this problem?
You should create a class to represent the a generic stock and then use DeserializeObject to deserialize it into a Dictionary(Of String, [classname]) where the Key represents the stock symbol and the value represents the class.
Take a look at this example:
Public Class Stock
Public Property assetType As String
Public Property symbol As String
Public Property description As String
Public Property bidPrice As Double
End Class
'...
Dim stocks = JsonConvert.DeserializeObject(Of Dictionary(Of String, Stock))(response)
Example: Live Demo
First thanks for the comments.
I ended up to do it completely different now...
I had further problems with the Ameritrade API:
Some fields are named with leading numbers (52WkHigh and 52WkLow) and
vb.net dev's know, that VB.net don't like properties in classes that
are named with a leading number
So I had to "patch" the received Json data and change the names on the fly to other names ("52WkHigh" to "dble52WkHigh" and "52WkLow" to "dble52WkLow") to be able to deserialize
over the data class, what is not nice
Further, I finally need the data (as fast as possible) in a data table and had "a long way to go":
get data -> deserialize to the data class -> walk thru the data class and overtake the data in the data table.
So.. my new solution (with JObject):
Note: needs:
Imports Newtonsoft.Json.Linq
Code snippets:
Create data table in memory:
Dim dtErgebnis As New DataTable
Dim drTemp As DataRow
With dtErgebnis.Columns
.Add("symbol", System.Type.GetType("System.String"))
.Add("lastPrice", System.Type.GetType("System.Double"))
.Add("lastSize", System.Type.GetType("System.Int32"))
.Add("quoteTime", System.Type.GetType("System.DateTime")) ' Note: is a Long in Json
...
End With
Parse the Json-String and fill the datatable:
get the data over httpclient (in JsonString)...
Dim oJson As JObject = JObject.Parse(JsonString) ' creates children tokens
Dim results As List(Of JToken) = oJson.Children().ToList
For Each item As JProperty In results
item.CreateReader()
drTemp = dtErgebnis.NewRow() ' create a new row to data table in memory
' Fill the fields
drTemp("symbol") = item.Value("symbol")
drTemp("lastPrice") = item.Value("lastPrice")
drTemp("lastSize") = item.Value("lastSize")
drTemp("quoteTime") = GetUTCDateFromTimeStamp(item.Value("quoteTimeInLong")).AddHours(1) ' original Long
...
' Add the new row to the data table
dtErgebnis.Rows.Add(drTemp)
' Save the changes
dtErgebnis.AcceptChanges()
Next
Additional note: The Ameritrade API gives back the time stamps as long (additional hurdle), but I (and I think also you;-) want it as datetime.
Therefore the Long (I think this data type comes from Java/Unix) has to be "translated" to datetime = vb.net function GetUTCDateFromTimeStamp below:
Public Function GetUTCDateFromTimeStamp(TimeStamp As Long) As DateTime
Static startTime As New DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)
Return startTime.AddMilliseconds(TimeStamp)
End Function
Additional note:
As I want to have the Swiss time, I add one hour to the UTC time.
So.. this a real good solution for me (exactly for the Ameritrade API).
And.. it's blazing fast... (I get 19 tickers with all fields and show the result (data table) in a data grid).
All together took < 1 Second ("felt" about 500 ms)
Hope this helps somebody...

Json conversion to double and bind to WPF nummericupdown fails

Currently i get an error on a binding. The situation is that i write my settings to a JSON file. When the app opens again the JSON file is read and used throughout the application. Now there's a strange thing: When i bind a double value to the value of a nummericupdown than i get an error: type 'JValue' to type 'System.Nullable1[System.Double]'for 'en-US' however this error doesn't occur when i recreate the JSON list and file. (simply said when i delete the file and restart the app it will create a new instance of a class en write it to disk)
Property in class:
Public Property SomeValue As Double
Write/Reader JSON:
'Write
Using _file As StreamWriter = New StreamWriter(SettingFilePath)
Dim serializer As New JsonSerializer()
serializer.Formatting = Formatting.Indented
serializer.Serialize(_file, Me)
End Using
'Read
Return JsonConvert.DeserializeObject(Of Settings)(File.ReadAllText(settingsfile))
JSON string:
"SomeValue": 1.0,
Binding in XAML:
<Controls:NumericUpDown
Width="200"
HorizontalAlignment="Center"
Maximum="5"
Minimum="1"
NumericInputMode="All"
Speedup="false"
Value="{Binding SomeValue}" />
Please note that i use the Mathapps Metro nummericupdown control version 1.6.5
Newtonsoft version 10.0.0.1 (Cannot update due to dependencies)
Edit:
As asked i digged deeper and now know where it starts, but don't know yet how to resolve it. Is start with my class for example:
Public class Hello
Dim a as Object
Dim b as EnumTypeOfObjectIn_A
Dim SomeOtherStuff as String
End class
Now when i DeserializeObject de file to the Class Hello then variable a becomes a object of type JObject and this is why alot of logica afterwords goes wrong. When i create the object in code everything goes well because the TypeOf object matches the one i put in. So is there a work arround for the Deserializer to convert the object to the one that is indicated in variable b ?
Found the solution i was looking for. The Newtonsoft JSON contains the JsonSerializerSettings class that helps the de/serialization process. For me it is important to add the Type of object when serializing so that's exactly what TypeNameHandling and TypeNameAssemblyFormatHandling is for in the Newtonsoft JSON assembly
I ended up with this code:
Public Class Hello
Public Property A As Object
Dim settingsfile As String = "C:\jsontest.json"
Public Sub Save()
Using _file As StreamWriter = New StreamWriter(settingsfile)
_file.Write(JsonConvert.SerializeObject(Me, Formatting.Indented, New JsonSerializerSettings() With {
.TypeNameHandling = TypeNameHandling.Objects,
.TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple
}))
End Using
End Sub
Public Function Load() As Hello
Return JsonConvert.DeserializeObject(Of Hello)(File.ReadAllText(settingsfile), New JsonSerializerSettings() With {.TypeNameHandling = TypeNameHandling.Objects})
End Function
End Class
Public Class Person
Public Property Name As String
Public Property Age As Integer
Sub New()
Me.Name = "John"
Me.Age = 130
End Sub
End Class
Producing this JSON output, note the $Type
{
"$type": "MyNamespace.Hello, MyNamespace",
"A": {
"$type": "MyNamespace.Person, MyNamespace",
"Name": "John",
"Age": 130
}
}

How do I parse this JSON using VB.net?

So, I'm working on a school project, and I'm trying to figure out the best way to deal with this data file that contains a pretty large amount of JSON objects. I know the basics of VB.net, basic event handling, etc.
I know the basics of designing Structures, and stuff like that, but I need to figure out how to parse and create a list of objects from a 5MB JSON file that contains entries such as the following:
{
"Air Elemental":{
"layout":"normal",
"name":"Air Elemental",
"manaCost":"{3}{U}{U}",
"cmc":5,
"colors":[
"Blue"
],
"type":"Creature — Elemental",
"types":[
"Creature"
],
"subtypes":[
"Elemental"
],
"text":"Flying",
"power":"4",
"toughness":"4",
"imageName":"air elemental"
},
"Ancestral Recall":{
"layout":"normal",
"name":"Ancestral Recall",
"manaCost":"{U}",
"cmc":1,
"colors":[
"Blue"
],
"type":"Instant",
"types":[
"Instant"
],
"text":"Target player draws three cards.",
"imageName":"ancestral recall"
},
"Animate Artifact":{
"layout":"normal",
"name":"Animate Artifact",
"manaCost":"{3}{U}",
"cmc":4,
"colors":[
"Blue"
],
"type":"Enchantment — Aura",
"types":[
"Enchantment"
],
"subtypes":[
"Aura"
],
"text":"Enchant artifact\nAs long as enchanted artifact isn't a creature, it's an artifact creature with power and toughness each equal to its converted mana cost.",
"imageName":"animate artifact"
}
}
If anyone could be of assistance, or just sort of point me in the right direction, I'd really appreciate it. I think the part that's throwing me off the most is that each card name is a key in itself, and all of the card's data is the value associated with the name "key"...
In this case it hardly matters how many there are because they are all structured the same way:
Public Class Spell
Public Property layout As String
Public Property name As String
Public Property manaCost As String
Public Property cmc As Integer
Public Property colors As String()
Public Property type As String
Public Property types As String()
Public Property subtypes As String()
Public Property text As String
Public Property power As String
Public Property toughness As String
Public Property imageName As String
End Class
I have no idea what the data represents, it looks like some fantasy game. When deserialized (I used Newtonsoft), you will end up with a Dictionary of spells with the key being "Air Elemental" and "Animate Artifact" etc. It takes but one line of code:
Dim jstr As String = from whereever
Dim mySpells = JsonConvert.DeserializeObject(Of Dictionary(Of String, Spell))(jstr)
Using the NET JavaScriptSerializer is about the same:
Dim jss As New JavaScriptSerializer
Dim myspells = jss.DeserializeObject(jstr)
You can use http://jsonutils.com/, or even Paste Special in VS, to help parse what the structures (classes, really) need to look like. However, it pays to use your brain and look at them. A robot will create 3 classes for that JSON and another class container to hold them. If there were 100 of them it would be needlessly long.
Since each item is identical and the JSON one class can be used for each. Since the JSON is formatted to accommodate, a standard NET dictionary works fine.
There is a wonderful feature in VS 2013 called "Paste as JSON" under the Edit menu, so copy your JSON string and choose this feature.
Be aware that there is a small bug that I reported to MS in the way that it declares arrays. The text it gives you will be
Public Property x() as DataType
however it needs to be changed to
Public Property x as DataType()
in order to be correctly declared as an array.

Json to vb.net string (Newtonsoft.Json)

hi i have this json string
[ { "desc" : "Unlike other foxes that roamed the woods of southern Ionia, Ahri had always felt a strange connection to the magical world around her; a connection that was somehow incomplete. Deep inside, she felt the skin she had been born into was an ill fit for her and dreamt of one day...",
"id" : 103,
"name" : "Ahri",
"tags" : [ "assassin",
"mage",
"ranged"
]
},
{ "desc" : "There exists an ancient order originating in the Ionian Isles dedicated to the preservation of balance. Order, chaos, light, darkness -- all things must exist in perfect harmony for such is the way of the universe. This order is known as the Kinkou and it employs a triumvirate...",
"id" : 84,
"name" : "Akali",
"tags" : [ "assassin",
"melee",
"stealth"
]
},
{ "desc" : "As the mightiest warrior to ever emerge from the Minotaur tribes of the Great Barrier, Alistar defended his tribe from Valoran's many dangers; that is, until the coming of the Noxian army. Alistar was lured from his village by the machinations of Keiran Darkwill, General Boram...",
"id" : 12,
"name" : "Alistar",
"tags" : [ "tank",
"pusher",
"melee"
]
}
i want to get the name property from the id property
like i want to convert the id 103 to champion and the end result will give me "Ahri"
this is my code so far
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim json As JsonConverter
Dim ent As champion = TryCast(JsonConvert.DeserializeObject(Of champion)(json), champion)
MsgBox(ent.name)
End Sub
End Class
Public Class champion
Public Property desc As String
Public Property id As Integer
Public Property name As String
Public Property tags As List(Of tags)
End Class
Public Class tags
Public Property type As Array
original json : https://gist.githubusercontent.com/nagash/1688617/raw/2a98e542aaabfc03f6db4328bd4dc98fc263df5a/lol-champions.json
But it gets this error
An unhandled exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll
Additional information: Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'json_test.champion' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.

ArgumentOutOfRange exception when deserializing to a DataSet with Json.Net

I have the following JSON string in a variable called strJSON.
{
"results":[
{
"templateName":"HUD Section 8",
"userID":"2",
"mobileObjectId":"4582",
"source":"M",
"inspectionType":"A",
"notes":"Window in bedroom needs repair.",
"agencyID":"",
"requestDate":"2014-05-09 00:00:00",
"agencyName":"",
"inspectionTimeBegun":"2014-05-09 14:00:17",
"inspectionDate":"2014-05-09 14:30:00",
"inspectionID":135,
"inspectionTimeComplete":"2014-05-09 14:29:25",
"summaryDecision":"F",
"createdAt":"2014-05-09T18:29:35.050Z",
"updatedAt":"2014-05-09T18:29:35.050Z",
"objectId":"1FgtD6WT8Y",
"ACL":{
"*":{
"read":true
},
"cryZoU5gXJ":{
"write":true,
"read":true
}
}
}
]
}
When I call the following line of code...
ds = Newtonsoft.Json.JsonConvert.DeserializeObject(Of DataSet)(strJSON)
I get an exception with the message stating "Specified argument was out of the range of valid values"
The JSON string is created with the following REST API call to Parse.com.
strJSON = http.QuickGetStr(strURL)
I am using this elsewhere with success albeit with simpler Parse classes but I have gone through this JSON string carefully and can't see anything wrong.
Any ideas on what might be causing this error?
In order for Json.Net to deserialize into a DataSet, the JSON must be in a specific format, as described in this answer. Your JSON is close, but the problem is the ACL object. The DataTableConverter that Json.Net 5.0 uses expects all of the columns in the table to be simple data types or it will throw an ArgumentOutOfRangeException (source). Json.Net 6.0 supports nested data tables and arrays in addition to simple types, but your ACL data still does not meet the required format that would allow it to be deserialized correctly to a DataSet. You have a few different options for dealing with this:
Change the JSON
If you control the format of the JSON (i.e. it is not from a third party) you can change it such that Json.Net 6.0 will be able to deserialize it to a DataSet. Here is what it would need to look like for that to work:
{
"results": [
{
"templateName": "HUD Section 8",
"userID": "2",
"mobileObjectId": "4582",
"source": "M",
"inspectionType": "A",
"notes": "Window in bedroom needs repair.",
"agencyID": "",
"requestDate": "2014-05-09 00:00:00",
"agencyName": "",
"inspectionTimeBegun": "2014-05-09 14:00:17",
"inspectionDate": "2014-05-09 14:30:00",
"inspectionID": 135,
"inspectionTimeComplete": "2014-05-09 14:29:25",
"summaryDecision": "F",
"createdAt": "2014-05-09T18:29:35.050Z",
"updatedAt": "2014-05-09T18:29:35.050Z",
"objectId": "1FgtD6WT8Y",
"ACL": [
{
"user": "*",
"read": true,
"write": false
},
{
"user": "cryZoU5gXJ",
"read": true,
"write": true
}
]
}
]
}
With this format, the ACL column of the results table will contain a nested DataTable with the individual ACL rows, each row having three columns, user, read and write.
Deserialize to strongly-typed classes
Instead of deserializing into a DataSet, you could deserialize into a set of strongly-typed classes. The advantage to this approach is that everything is in an easily usable form. The disadvantage is that you need to know what is in the JSON before you can create the classes.
You can use third-party tools like json2csharp.com to help generate the classes from a sample of the JSON, as was suggested in another answer (now deleted), but note that this is not foolproof (and it doesn't do VB). Sometimes you will need to intervene and edit the classes manually. For example, if generate classes from the JSON in your question, you'll notice that it creates a fixed class for each ACL instance. This will not work unless your set of ACLs always has exactly two items, one called Everyone and the other CryZoU5gXJ. I think it is much more likely that the set of ACLs will be variable, so it makes sense to use a Dictionary for these. Here are the classes I would propose:
Class RootObject
Public Property results As List(Of Result)
End Class
Class Result
Public Property templateName As String
Public Property userID As String
Public Property mobileObjectId As String
Public Property source As String
Public Property inspectionType As String
Public Property notes As String
Public Property agencyID As String
Public Property requestDate As String
Public Property agencyName As String
Public Property inspectionTimeBegun As String
Public Property inspectionDate As String
Public Property inspectionID As Integer
Public Property inspectionTimeComplete As String
Public Property summaryDecision As String
Public Property createdAt As String
Public Property updatedAt As String
Public Property objectId As String
Public Property ACL As Dictionary(Of String, ACL)
End Class
Class ACL
Public Property read As Boolean
Public Property write As Boolean
End Class
With this class structure in place, you can deserialize like this:
Dim root As RootObject = JsonConvert.DeserializeObject(Of RootObject)(strJSON)
For the ACLs, the key for each dictionary entry will be the user ID (or * as you have in your example). If you don't actually care about the ACLs, you can simply omit the ACL property from the Result class. By default Json.Net will skip properties that exist in the JSON but do not exist in the class.
Use the LINQ-to-JSON API to parse the JSON
With Json.Net there is always more than one way to skin the cat. Json.Net's LINQ-to-JSON API really shines when the JSON you are parsing is highly variable and/or you don't want to create classes for receiving the data. You can deserialize any valid JSON to a hierarchy of JToken objects and then pick them apart however you need. For example, if you just needed a few select pieces of information from each result, you could do this:
Dim token As JToken = JToken.Parse(json)
For Each result As JObject In token("results").Children(Of JObject)()
Console.WriteLine("userID: " + result("userID").ToString())
Console.WriteLine("templateName: " + result("templateName").ToString())
Console.WriteLine("inspectionID: " + result("inspectionID").ToString())
Console.WriteLine("inspectionType: " + result("inspectionType").ToString())
Console.WriteLine("inspectionDate: " + result("inspectionDate").ToString())
Console.WriteLine("summaryDecision: " + result("summaryDecision").ToString())
Console.WriteLine("notes: " + result("notes").ToString())
Next
You could use this same approach to manually build a DataSet from the JSON. Here is a generic function that will deserialize JSON into a DataSet but ignore any complex objects (e.g. the ACLs) instead of throwing an exception:
Function DeserializeToDataSet(json As String) As DataSet
Dim root As JObject = JObject.Parse(json)
Dim ds As DataSet = New DataSet()
For Each prop As JProperty In root.Properties
If prop.Value.Type = JTokenType.Array Then
Dim dt As DataTable = ds.Tables.Add(prop.Name)
For Each row As JObject In prop.Value.Children(Of JObject)()
Dim dr As DataRow = dt.NewRow
For Each col As JProperty In row.Properties
Dim colType As Type = GetColumnType(col.Value.Type)
If Not colType Is Nothing Then
Dim dc As DataColumn = dt.Columns(col.Name)
If dc Is Nothing Then
dc = dt.Columns.Add(col.Name, colType)
End If
dr(col.Name) = col.Value.ToObject(colType)
End If
Next
dt.Rows.Add(dr)
Next
End If
Next
Return ds
End Function
Function GetColumnType(tokenType As JTokenType) As Type
If tokenType = JTokenType.String Then Return GetType(String)
If tokenType = JTokenType.Integer Then Return GetType(Integer)
If tokenType = JTokenType.Date Then Return GetType(DateTime)
If tokenType = JTokenType.Boolean Then Return GetType(Boolean)
If tokenType = JTokenType.Float Then Return GetType(Double)
Return Nothing
End Function
Of course if you need the ACLs, you'll need to customize this method to get that data into a form that is consumable by your code. I'll leave that part to you.
Json.Net will only parse directly into a DataSet if it conforms to a certain standard. See this answer for the layout it needs.
However, you could deserialize to an XML document and use the DataSet object's ReadXml method load it for you. See this question for details on how to do this.
(HT to Brian Rogers for dataset structure details)