How do DataContracts work? - Deserialize Json - asp.net

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.

Related

Create a dataContract in separe file problem

Hi I need to create a class for return the data in WCF service. I followed the web at 5 simple steps to create your first RESTful service. However I get the error for . I searched the web and add the System.Runtime.Serialization.DataContractSerializer, Would someone tell me what should do. I am using VS2015 as the tool to build it. Thanks.
Imports System.Runtime.Serialization
Imports System.Collections.Generic
Imports System.Runtime.Serialization.DataContractSerializer
<DataContract>
Public Class Locations
<DataMember>
Public Property LocationName As String
<DataMember>
Public Property LocationID As Integer
End Class
Could you please share the error details with me?
As you know, we usually use the datacontract to transmit the complex data type which could be recognized by the client-side and server-side. so that the data could be serialized and transmitted normally between different platforms.
For the restful web service in WCF, we need to use the Webhttpbinding build the data channel and add the Webhttpbehavior to the service endpoint.
I have made a demo, wish it is useful to you.
Server-side.
Imports System.Runtime.Serialization
Imports System.ServiceModel
Imports System.ServiceModel.Description
Imports System.ServiceModel.Web
Module Module1
Sub Main()
Dim uri As New Uri("http://localhost:900")
Dim binding As New WebHttpBinding()
binding.CrossDomainScriptAccessEnabled = True
binding.Security.Mode = WebHttpSecurityMode.TransportCredentialOnly
binding.Security.Transport.ClientCredentialType = HttpClientCredentialType.None
Using sh As New ServiceHost(GetType(MyService), uri)
Dim se As ServiceEndpoint = sh.AddServiceEndpoint(GetType(IService), binding, uri)
se.EndpointBehaviors.Add(New WebHttpBehavior())
sh.Open()
Console.WriteLine("Service is ready")
Console.ReadLine()
sh.Close()
End Using
End Sub
<ServiceContract([Namespace]:="mydomain")>
Public Interface IService
<OperationContract>
<WebGet(ResponseFormat:=WebMessageFormat.Json)>
Function SayHello() As List(Of Product)
End Interface
Public Class MyService
Implements IService
Public Function SayHello() As List(Of Product) Implements IService.SayHello
Dim result = New List(Of Product)() From {
New Product With {
.Id = 1,
.Name = "Apple"
},
New Product With {
.Id = 2,
.Name = "Pear"
}
}
Return result
End Function
End Class
<DataContract([Namespace]:="mydomain")>
Public Class Product
<DataMember>
Public Property Id() As Integer
<DataMember>
Public Property Name() As String
End Class
End Module
Client.
$(function(){
$.ajax({
type:"GET",
url:"http://10.157.18.188:900/sayhello",
dataType:"jsonp",
success:function(d){
$.each(d,function(i,o){
console.log(o.Id);
console.log(o.Name);
})
}
})
})
Result.
Here is an official sample
https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-create-a-basic-wcf-web-http-service

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.

Deserialize JSON in ASP.NET with VB

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)

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")

VB.Net Initialising a class using System.Reflection and System.Type to create a session based singleton extension method

I have had several occasions recently to access a specific class several times over a relatively small time frame.
So I've been storing the value of the class in Session and trying to access it on page load, if it's not available creating a new instance and storing that in session.
So instead of constantly replicating the same code for different classes on different pages I'm trying to create an extension method to do this for me.
I want to use it like this
Dim objName as MyClass
objName.SessionSingleton()
So far this is what I have for my extension method:
<Extension()> _
Public Sub SessionSingleton(ByRef ClassObject As Object)
Dim objType As Type = ClassObject.GetType
Dim sessionName As String = objType.FullName
If TypeOf HttpContext.Current.Session(sessionName) Is objType And HttpContext.Current.Session(sessionName) <> "" Then
ClassObject = HttpContext.Current.Session(sessionName)
Else
Dim singleton As Object = New objType???????
HttpContext.Current.Session(sessionName) = singleton
ClassObject = singleton
End If
End Sub
I'm stuck on what to do when I make my new instance of my class (it would have to have a New() sub)
I'm not sure where to go from here... or even if this is the best way to do it.
I figured it out and am posting my code for reference. While digging thru pages about Class/Object Factories (thanks RBarry) I found several references to Activator.CreateInstance() in the System.Reflection Class I came up with this.
Imports Microsoft.VisualBasic
Imports System.Runtime.CompilerServices
Imports System.Reflection
Public Module enviornmentUtilities
<Extension()> _
Public Function SessionSinglton(ByVal objType As Type) As Object
Dim sessionName As String = objType.FullName.ToString
If Not HttpContext.Current.Session(sessionName) Is Nothing Then
HttpContext.Current.Trace.Write(HttpContext.Current.Session(sessionName).ToString)
Return HttpContext.Current.Session(sessionName)
Else
Dim ss = Activator.CreateInstance(objType)
HttpContext.Current.Session(sessionName) = ss
Return ss
End If
End Function
End Module
This will let you create a session based singleton from any class that does not require parameters in the new method (which isn't required for this to work)
To test I made a simple Class:
Public Class HasNew
Public FreshInstance As Boolean = True
Public Sub New()
HttpContext.Current.Trace.Warn("This Class has a new method")
End Sub
Public Sub CheckFreshness()
If FreshInstance Then
HttpContext.Current.Trace.Warn("Fresh HasNew Instance")
FreshInstance = False
Else
HttpContext.Current.Trace.Warn("NotFresh HasNew Instance")
End If
End Sub
Public Shared Function type() As Type
Return GetType(HasNew)
End Function
Public Shared Function SessionSinglton() As HasNew
Return GetType(HasNew).SessionSinglton
End Function
End Class
You'll notice the two Public Shared Methods type() and SessionSinglton which calls the above extension method.
With those two functions added we have three ways to initiate the Session Singlton demonstrated here:
Dim HN As HasNew
HN = HasNew.SessionSinglton
HN.CheckFreshness()
HN = HasNew.type.SessionSinglton
HN.CheckFreshness()
HN = GetType(HasNew).SessionSinglton
HN.CheckFreshness()
The Trace Output for this file is as follows:
This Class has a new method
Fresh HasNew Instance
NotFresh HasNew Instance
NotFresh HasNew Instance
The classes new() method is accessed on the first call to the SessionSinglton method and subsequent calls reflect that the instance is in fact being pulled from memory.
I hope this helps someone else in the future.
If you used generics you could just do New T(). Also your SessionSingleton returns "object" type, requiring casting. I did not test this but it should work.
Imports Microsoft.VisualBasic
Imports System.Runtime.CompilerServices
Imports System.Reflection
Public Module enviornmentUtilities
<Extension()> _
Public Function SessionSinglton(Of T As {Class, New})(ByVal obj As T) As T
Dim sessionName As String = obj.GetType.Name
If Not HttpContext.Current.Session(sessionName) Is Nothing Then
HttpContext.Current.Trace.Write(HttpContext.Current.Session(sessionName).ToString)
Return HttpContext.Current.Session(sessionName)
Else
Dim ss = New T()
HttpContext.Current.Session(sessionName) = ss
Return ss
End If
End Function
End Module

Resources