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.
Related
I'm trying to generate some JSON that looks like this:
{
"#type": "MessageCard",
"sections": [
{
"activityTitle": " Request",
"facts": [
{
"name": "name1",
"value": "Value"
},
{
"name": " Date:",
"value": "Value Date"
}
],
"text": "Some Test."
}
],
"potentialAction": [
{
"#type": "ActionCard",
"name": "Add a comment",
"inputs": [
{
"#type": "TextInput",
"id": "comment",
"isMultiline": true
}
]
}
]
}
I performed a paste special into VS and it generated the class structure for me as such:
Public Class MessageCard
Public Property type As String
Public Property context As String
Public Property summary As String
Public Property themeColor As String
Public Property sections() As Section
Public Property potentialAction() As Potentialaction
End Class
I'm trying to add the sections to the object as such:
Dim m as New MessageCard
Dim s As New List(Of Section)
s.Add(s1)
s.Add(s2)
m.sections = s
The compiler complains that it cannot convert a list of Sections into a Section. Did the class get generated incorrectly, or am i constructing it incorrectly?
First, your JSON is not quite complete and the Classes you show wont create that JSON.
As posted, that JSON simply shows a Sections and potentialAction class which are not related in any way. An enclosing [ ... ] is needed to represent the MessageCard class containing the two of them.
[{
"#type": "MessageCard",
...
}]
Next, the class you have shows all sorts of things not present in the JSON: context, summary and themeColor for instance. I assume those might be missing for brevity, but it is confusing. There is also 2 other Types missing which are in the JSON, Fact and Input.
Corrected, the classes should be:
Public Class MsgCard
<JsonProperty("#type")>
Public Property ItemType As String
Public Property sections As List(Of Section)
Public Property potentialAction As List(Of Potentialaction)
Public Sub New()
sections = New List(Of Section)
potentialAction = New List(Of Potentialaction)
End Sub
End Class
Public Class Section
Public Property activityTitle As String
Public Property facts As Fact()
Public Property text As String
End Class
Public Class Fact
Public Property name As String
Public Property value As String
End Class
Public Class Potentialaction
<JsonProperty("#type")>
Public Property ActionType As String
Public Property name As String
Public Property inputs As Input()
End Class
Public Class Input
<JsonProperty("#type")>
Public Property InputType As String
Public Property id As String
Public Property isMultiline As Boolean
End Class
Notes
You did not specify a JSON serializer, so this is prepared to JSON.NET as recommended by Microsoft.
#type is an illegal property name, so the JsonProperty attribute is used to create an alias. I also used less confusingly redundant names.
You may want to change Fact and Input to List(Of T) if you will be creating and pushing them into the class object as well.
Finally, for the actual question you asked, most of the automatic class generators have trouble with arrays (even VS).
Public Property sections() As Section
' should be:
Public Property sections As Section()
That simply declares that sections will be an array, it does not create the array. Normally this is not a problem because the Serializer/Deserializer will create the array. To allow code external to the class to add to them, you probably want to use a List as the classes above do, then create the instance in the constructor:
Public Sub New()
sections = New List(Of Section)
potentialAction = New List(Of Potentialaction)
End Sub
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.
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.
I've got some JSON coming from a webservice looks like this:
{
"disclaimer": "Exchange r..",
"license": "Data sourced from variou..",
"timestamp": 1262365200,
"base": "USD",
"rates": {
"AED": 3.67275,
"AFN": 48.550089,
"ALL": 96.435505,
"AMD": 377.894224,
"ANG": 1.791,
"AOA": 89.174867,
"ARS": 3.79928
}
}
I've built a little class to accept it.
Class currencyValues
Class ratePairs
Property currencyCode
Property currencyValue
End Class
Property disclaimer
Property license
Property timestamp
Property base
Property rates As New List(Of ratePairs)
End Class
When I run the code to accept the JSON into the class it takes the top level properties, but the list of ratePairs does not populate.
Dim js As New System.Web.Script.Serialization.JavaScriptSerializer()
Dim recs = js.Deserialize(Of currencyValues)(curRecordJSON)
The count of list recs.Rates is zero.
What am I doing wrong?
The rates property in the original json is not an array, and therefore can't be deserialised to a list.
It is in fact an object, with properties such as AED and AFN etc. You are able to deserialise it as a Dictionary(Of String, Double), or if the properties never change, you could build a class to hold it:
Class Rates
Property AED
Property AFN
'etc
End Class
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)