Deserialize JSON in ASP.NET with VB - asp.net

I'm using VB in ASP.NET and I've been looking at trying to deserialize the below JSON for about 4 days now with no success.
The problem is the code I have below is returning null values. I would like to get results of the JSON for each class member I've declared including price and shipping values for each product.
Can anyone point me in the right direction with regard to
1.) why I keep getting null values back and
2.) if the classes I declared are valid for the json I am trying to deserialize?
Any help is greatly appreciated!
Here is an example of the JSON I am working with:
{
"kind": "shopping#products",
"items":
[{
"kind": "shopping#product",
"id": "tag:google.com,2010:shopping/products/8040/8382012077897342942",
"product": {
"googleId": "8382012077897342942",
"title": "LEGO Star Wars™: Jabba's Palace™ (9516)",
"description": "Rescue Han Solo from Jabba the Hutt's desert palace!",
"inventories": [
{
"price": 119.99,
"shipping": 12.95,
"currency": "USD"
}
]
}
}
]
}
Here is the Code I have currently:
Imports System.Web.Script.Serialization
Imports Newtonsoft.Json.Linq
Public Class inventories
Public Property price As Double
Public Property shipping As Double
End Class
Public Class product
Public Property googleid As String
Public Property title As String
Public Inventories As inventories()
End Class
Public Class Items
Public Property product As product()
Public Property kind As String
Public Property id As String
End Class
Partial Class JSON_Test
Inherits System.Web.UI.Page
Protected Sub getbutton_Click(sender As Object, e As System.EventArgs) Handles getbutton.Click
' 1. Get JSON string from Google
Dim google_json_string As String
google_json_string = json_text.Text
'2. Deserialize JSON - Method 1
Dim jss As New JavaScriptSerializer
Dim ds_results = jss.Deserialize(Of List(Of Items))(google_json_string)
result1.Text = ds_results.Count
result2.Text = ds_results(0).kind
End Sub
End Class
(Update) I've updated the code to include classes that are structured like this (thanks Dan-o):
Public Class g_JSON
Public Property items As List(Of Items)
End Class
Public Class Items
Public Property product As product()
Public Property kind As String
Public Property id As String
End Class
Public Class product
Public Property googleid As String
Public Property title As String
Public Inventories As inventories()
End Class
Public Class inventories
Public Property price As Double
Public Property shipping As Double
End Class
I also updated the code to read as follows:
Partial Class JSON_Test
Inherits System.Web.UI.Page
Protected Sub getbutton_Click(sender As Object, e As System.EventArgs) Handles getbutton.Click
' 1. Get JSON string from Google
Dim google_json_string As String
google_json_string = json_text.Text
'2. Deserialize JSON - Method 1
Dim jss As New JavaScriptSerializer
Dim ds_results = jss.Deserialize(Of g_JSON)(google_json_string)
result1.Text = ds_results.items(0).id
result2.Text = ds_results.items(0).kind
End Sub
End Class
Now, the code will compile without issue but when I kick off the click event I get the following error:
No parameterless constructor defined for type of 'product[]'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.MissingMethodException: No parameterless constructor defined for type of 'product[]'.
Source Error:
Line 38: Dim ds_results = jss.Deserialize(Of g_JSON)(google_json_string)
What does that mean and how do I create a parameterless constructor for product()?
Thanks for taking a look!

According to jsonlint your json isn't valid (comma after "inventories" array).

You forgot the container.. the class that holds everything...
Class something
Public property kind as string = String.Empty
Public property items as list(of Item) = Nothing
End Class
Then you deserialize to something, not list(of item)

Related

difficulty serializing a collection in web api 2 using knockoutjs

I have a knockoutjs web page doing a post to a VB .net Web API. for some reason my collection always shows nothing on the controller.
on the web page in the post here is my data.
data: ko.toJSON({
ShpmntCntrlNbr: self.ShpmntCntrlNbr,
MstrBillNbr: self.MstrBillNbr,
accesorialChangesHaveBeenMade: self.accesorialChangesHaveBeenMade,
DB2ACTCollection: actCollection
}),
this shows in the console (chrome developer tools) like this.
{
"ShpmntCntrlNbr":"11019813",
"MstrBillNbr":" ",
"accesorialChangesHaveBeenMade":true,
"DB2ACTCollection":[
{"ShpmntCntrlNbr":"11019813"}
]
}
here is .net VB controller
Public Function Post(data As VMFreightReleaseSaveDB2ChangesInput) As IHttpActionResult
Return Ok()
End Function
unfortunately when I put a stop sign in here and inspect data. I get
DB2ACTCollection Nothing
MstrBillNbr " "
ShpmntCntrlNbr "1101983"
accesorialChangesHaveBeenMade True
no clue why my DBACTCollection is showing as nothing. any thoughts?
and here are the classes.
Public Class VMFreightReleaseSaveDB2ChangesInput
Public Property ShpmntCntrlNbr() As String
Get
Return m_ShpmntCntrlNbr
End Get
Set(value As String)
m_ShpmntCntrlNbr = value
End Set
End Property
Private m_ShpmntCntrlNbr As String
Public Property MstrBillNbr() As String
Get
Return m_MstrBillNbr
End Get
Set(value As String)
m_MstrBillNbr = value
End Set
End Property
Private m_MstrBillNbr As String
Public Property accesorialChangesHaveBeenMade() As Boolean
Get
Return m_accesorialChangesHaveBeenMade
End Get
Set(value As Boolean)
m_accesorialChangesHaveBeenMade = value
End Set
End Property
Private m_accesorialChangesHaveBeenMade
Public Property DB2ACTCollection() As List(Of DB2_ACT2)
Get
Return m_DB2ACTCollection
End Get
Set(value As List(Of DB2_ACT2))
value = m_DB2ACTCollection
End Set
End Property
Private m_DB2ACTCollection As List(Of DB2_ACT2)
and
Public Class DB2_ACT2
Public Property ShpmntCntrlNbr() As String
Get
Return m_ShpmntCntrlNbr
End Get
Set(value As String)
m_ShpmntCntrlNbr = value
End Set
End Property
Private m_ShpmntCntrlNbr As String
End Class
Just a note here is my solution so far, not real happy with it but it seems to be working. instead of data As VMFreightReleaseSaveDB2ChangesInput I have data as JToken. then I just loop through it although I am still at a loss why the native .net web api serializer is not doing this.
Dim inner As JArray = data("DB2ACTCollection")
Dim ACTCollection = New List(Of DB2_ACT)
For Each item As JObject In inner
Dim ACT As New DB2_ACT
ACT.ShpmntCntrlNbr = item("ShpmntCntrlNbr")
ACTCollection.Add(ACT)
Next
Based on the JSON and seeing as the models are POCOs try using auto properties with DB2ACTCollection as an array and see if that deserializes properly
Public Class DB2_ACT2
Public Property ShpmntCntrlNbr As String
End Class
Public Class VMFreightReleaseSaveDB2ChangesInput
Public Property ShpmntCntrlNbr As String
Public Property MstrBillNbr As String
Public Property accesorialChangesHaveBeenMade As Boolean
Public Property DB2ACTCollection As DB2_ACT2()
End Class

Return Valid Enumeration List in ASP.NET Web API Error Response

When an API user sends a POST request that includes an invalid enumeration, is there a good way to provide a more helpful error response that includes the valid enumerations?
So if I have a sample Class with an Enumerator:
Public Class MyObject
Public Property MyProp As MyEnum
Public Enum MyEnum
Foo
Bar
End Enum
End Class
And an Action Filter:
Public Class ValidateModelAttribute
Inherits ActionFilterAttribute
Public Overrides Sub OnActionExecuting(actionContext As HttpActionContext)
If actionContext.ModelState.IsValid = False Then
actionContext.Response = actionContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, actionContext.ModelState)
End If
End Sub
End Class
If the user sends something like:
{
"MyProp": "NotValid"
}
The action filter sends an error response like:
{
"Message": "The request is invalid.",
"ModelState": {
"value.MyProp.MyEnum": [
"Error converting value \"NotValid\" to type 'API.MyObject+MyEnum'. Path 'MyObject.MyEnum', line 29, position 32."
]
}
}
I'd like to be able to send a more useful error response, such as:
{
"Message": "The request is invalid.",
"ModelState": {
"value.MyProp.MyEnum": [
"'NotValid' is not a valid value for MyProp; Valid values include 'Foo','Bar'"
]
}
}
Any thoughts on how I could handle this?
I created a custom converter to deserialize enumerations. The converter throws an error if it's unable to parse the JsonReader's value into the target enum.
Public Class EnumConverter
Inherits JsonConverter
Public Overrides Function CanConvert(objectType As Type) As Boolean
Return True
End Function
Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
Try
'if the enum parses properly, deserialize the object like usual
Dim tryParse As Object = [Enum].Parse(objectType, reader.Value)
Return serializer.Deserialize(reader, objectType)
Catch ex As Exception
'value wasn't valid - return a response stating such and indicating the valid values
Dim valid As String = String.Format("'{0}'", String.Join(",", [Enum].GetNames(objectType)).Replace(",", "','"))
Throw New FormatException(String.Format("'{0}' is not a valid value for {1}; Valid values include: {2}", reader.Value, reader.Path, valid))
Return Nothing
End Try
End Function
Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
'use the default serialization
serializer.Serialize(writer, value)
End Sub
End Class
...
Public Class MyObject
<JsonConverter(GetType(EnumConverter))> _
Public Property MyProp As MyEnum
Public Enum MyEnum
Foo
Bar
End Enum
End Class

VB.NET hide a property member

I am currently working on the return class. The problem is I want to show the certain member only when some of the condition meet. Below is my code. I only want to show ResponseMsg member when the ResponseCode is 99 otherwise it will be hidden.
Public Class LoginResponse
Public Property TerminalID As String
Public Property ReaderID As String
Public Property TransRef As String
Public Property TransDateTime As String
Public Property Timeout As Integer
Public Property ResponseCode As String
Public Property ResponseMsg As String
Public Property Cryptogram As String
End Class
You can't that I know of. But you can do something like this:
Public Property ResponseMsg
Get
If ResponseCode <> SomeCodeValue
Return _responseCode
Else
Return Nothing
End if
End Get
End Property
You might want to think about making a specialized class.
Let's say you have your basic LoginResponse
Public Class LoginResponse
Public Property TerminalID As String
Public Property ReaderID As String
Public Property TransRef As String
Public Property TransDateTime As String
Public Property Timeout As Integer
Public Property ResponseCode As String
' Note: no ResponseMsg here
Public Property Cryptogram As String
End Class
Then you'd have an extended response class inheriting your basic LoginResponse:
Public Class LoginResponseEx : Inherits LoginResponse
Public Property ResponseMsg As String
End Class
Then where ever you create those LoginResponse objects, you just create one of the apropriate.
Let's say you have a GetResponse() procedure like:
Public Function GetResponse() As LoginResponse
Dim result As LoginResponse = Nothing
Dim code As Integer = GetSomeCode()
' ... get the other properties
' Say you have a const or something with the appropriate code: SPECIAL_CODE
If code = SPECIAL_CODE Then
Dim msg As String = GetSomeMessage()
result = New LoginResponseEx(..., code, msg, ...) ' have a special Response
Else
result = New LoginResponse(..., code, ...) ' have a normal Response
End If
Return result
End Function
Finally when checking the response you just check whether you have a special value in ResponseCode and cast the object respectivly.
'...
Dim resp as LoginResponse = GetResponse()
If resp.ResponseCode = SPECIAL_CODE Then
Dim respx as LoginResponseEx = CType(resp, LoginResponseEx)
Console.WriteLine("ResponseMessage was: " & respx.ResponseMsg
Else
Console.WriteLine("No ResponseMessage")
End If
'...
This way you have your basic LoginResponse with the ResponseMsg hidden in the special class ResponseLoginEx
Note when you do this you should think about how you implement virtual classes. e.g. the fields might have to be declared as Protected instead of Private, though i'm sure you'll do fine.
This also works with Serializable classes, of course.

VB.NET: Use Class Name as Expression

I'm not sure if this is possible but I would like to associate a class name reference to a shared member method / property / variable. Consider:
Public Class UserParameters
Public Shared Reference As Object
Public Shared Function GetReference() As Object
Return Reference
End Function
End Class
In another part of the program I would like to simply call UserParameters and have it return Reference either by aliasing GetReference or the variable directly.
I am trying to emulate the Application, Request, or Session variable:
Session(0) = Session.Item(0)
Any suggestions would be greatly appreciated.
You can't return an instance member from a static method directly (the static method can't access instance members because it isn't instantiated with the rest of the class, only one copy of a static method exists).
If you need to setup a class in such a way that you can return an instance from a static method you would need to do something similar to the following:
Public Class SampleClass
Private Sub New()
'Do something here
End Sub
Public Shared Function GetSample() As SampleClass
Dim SampleClass As SampleClass
SampleClass = New SampleClass
SampleClass.Sample = "Test"
Return SampleClass
End Function
Private _SampleString As String
Public Property Sample As String
Get
Return _SampleString
End Get
Private Set(ByVal value As String)
_SampleString = value
End Set
End Property
End Class
Public Class SampleClass2
Public Sub New()
'Here you can access the sample class in the manner you expect
Dim Sample As SampleClass = SampleClass.GetSample
'This would output "Test"
Debug.Fail(Sample.Sample)
End Sub
End Class
This method is used in various places in the CLR. Such as the System.Net.WebRequest class. where it is instantiated in this manner in usage:
' Create a request for the URL.
Dim request As WebRequest = WebRequest.Create("http://www.contoso.com/default.html")

How do DataContracts work? - Deserialize Json

I grabbed an example off of this SO question, and built my own custom Google Maps object used for deserializing the json object.
Now the code works like a champ, but I just need an explanation on why/how it works. Does the serializer "try" to match up names, or is something else going on.
What exactly is this doing?
Here's the working code.
Imports System.Net
Imports System.Web
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Json
Imports System.Web.Script.Serialization
Namespace Utilities.Apis
Public NotInheritable Class GoogleGeolocate
Private Const googleUrl As String = "http://maps.googleapis.com/maps/api/geocode/json?address={0}&sensor=false"
Private Sub New()
End Sub
Public Shared Function GetLatLon(ByVal address As String) As String
''# This is just here to prevent "placeholder" data from being submitted.
If address = "6789 university drive" Then
Return Nothing
End If
address = HttpUtility.UrlEncode(address)
Dim url = String.Format(googleUrl, address)
Dim request = DirectCast(HttpWebRequest.Create(url), HttpWebRequest)
request.Headers.Add(HttpRequestHeader.AcceptEncoding, "gzip,deflate")
request.AutomaticDecompression = DecompressionMethods.GZip Or DecompressionMethods.Deflate
Dim serializer As New DataContractJsonSerializer(GetType(GoogleResponse))
Dim res = DirectCast(serializer.ReadObject(request.GetResponse().GetResponseStream()), GoogleResponse)
Dim resources As GoogleResponse.Result = res.results(0)
Dim point = resources.geometry.location.lat
Dim latlon As New GeolocationLatLon
With latlon
.latitude = resources.geometry.location.lat
.longitude = resources.geometry.location.lng
End With
Dim jsonSerializer = New JavaScriptSerializer
Return jsonSerializer.Serialize(latlon)
End Function
End Class
<DataContract()>
Public Class GoogleResponse
<DataMember()>
Public Property results() As Result()
<DataContract()>
Public Class Result
<DataMember()>
Public Property geometry As m_Geometry
<DataContract()>
Public Class m_Geometry
<DataMember()>
Public Property location As m_location
<DataContract()>
Public Class m_location
<DataMember()>
Public Property lat As String
<DataMember()>
Public Property lng As String
End Class
End Class
End Class
End Class
End Namespace
And here's the GeolocationLatLon Poco
Public Class GeolocationLatLon
Public latitude As String
Public longitude As String
End Class
When I call the code, it's really quite simple.
note, this is an MVC controller, that has nothing to do "really" with the question other than to show what I'm doing
Function GeoLocation(ByVal address As String) As ContentResult
Return New ContentResult With {.Content = GoogleGeolocate.GetLatLon(address),
.ContentType = "application/json"}
End Function
And the final result is
{"latitude":"50.124300","longitude":"-114.4979990"}
Internally, DataContractJsonSerializer maps JSON name/value pairs to an XML infoset. In fact, DataContractJsonSerializer is built on top of the XML-based DataContractSerializer and processes every JSON input and JSON output as if it were dealing with XML. There is a higher-level abstraction layer (a JSON writer and a JSON reader, as exposed via JsonReaderWriterFactory) that actually translates this XML to JSON and JSON back to internal XML.
See this excellent overview (Mapping Between JSON and XML) to see what happens to DataContractJsonSerializer internally and how it pulls all this off.

Resources