1
votes

I am trying to create a WCF Json Rest and consume the same in WPF Phone 8.1 App (not silverlight).

My WCF is:

<OperationContract()>
        <WebGet(UriTemplate:="getdata", ResponseFormat:=WebMessageFormat.Json)>
    Function DoWork() As Dictionary(Of Integer, String)

WCF Code:

Public Class BasicService
    Implements IBasicService

    Public Function DoWork() As Dictionary(Of Integer, String) Implements IBasicService.DoWork

        Dim k As New Dictionary(Of Integer, String)
        k.Add(1, "One")
        k.Add(2, "Two")

        Return k
    End Function


End Class

Phone Consumption Code:

Dim httpCli As New System.Net.Http.HttpClient()
        Dim httpres As Task(Of HttpResponseMessage) = httpCli.GetAsync("http://localhost:4149/BasicService.svc/getdata")
        Dim tk As Task(Of String)
        tk = httpres.Result.Content.ReadAsStringAsync

        Try
            Dim resultstring As String = tk.Result.Substring(tk.Result.IndexOf("{"), tk.Result.LastIndexOf("}") + 1 - tk.Result.IndexOf("{"))
            Dim DoWorkResult As Dictionary(Of Integer, String) = Newtonsoft.Json.JsonConvert.DeserializeObject(resultstring)

        Catch ex As Exception

        End Try

        Try

            Dim DoWorkResult As Dictionary(Of Integer, String) = Newtonsoft.Json.JsonConvert.DeserializeObject(tk.Result)

        Catch ex As Exception

        End Try

The Fiddler Data for WCF is : RAW:

HTTP/1.1 200 OK Cache-Control: private Content-Type: application/json; charset=utf-8 Server: Microsoft-IIS/8.0 X-AspNet-Version: 4.0.30319 X-SourceFiles: =?UTF-8?B?RTpcUmFnaGF2YVxpbXBcUHJvamVjdHNcTUNvbGxlY3RvclxNQ1dDRlxCYXNpY1NlcnZpY2Uuc3ZjXGdldGRhdGE=?= X-Powered-By: ASP.NET Date: Mon, 15 Jun 2015 22:50:53 GMT Content-Length: 49

[{"Key":1,"Value":"One"},{"Key":2,"Value":"Two"}]

WebView:

[{"Key":1,"Value":"One"},{"Key":2,"Value":"Two"}]

When deserializing in code:

Error at first Try Catch with result text from '{' to '}' : Additional text encountered after finished reading JSON content: ,. Path '', line 1, position 23.

Error at second Try Catch with not modified json string:

Unable to cast object of type 'Newtonsoft.Json.Linq.JArray' to type 'System.Collections.Generic.Dictionary`2[System.Int32,System.String]'.

Could you please correct me where I went wrong or what mistake did I do.

2

2 Answers

1
votes

You are using Json.NET, but your json string represents a dictionary in the format used by DataContractJsonSerializer as opposed to the simpler and more conventional Json.NET format.

One way to resolve this would be to switch to DataContractJsonSerializer, as is shown here: VB.net JSON Deserialize.

If you prefer to stick with Json.NET, you will need to create a custom JsonConverter such as:

Public Class KeyValueDictionaryConverter(Of TKey, TValue)
    Inherits JsonConverter
    Public Overrides Function CanConvert(objectType As Type) As Boolean
        Return GetType(IDictionary(Of TKey, TValue)).IsAssignableFrom(objectType)
    End Function

    Public Overrides Function ReadJson(reader As JsonReader, objectType As Type, existingValue As Object, serializer As JsonSerializer) As Object
        Dim array = serializer.Deserialize(Of KeyValuePair(Of TKey, TValue)())(reader)

        If array Is Nothing Then
            Return existingValue
        End If

        Dim dict = (If(TryCast(existingValue, IDictionary(Of TKey, TValue)), DirectCast(serializer.ContractResolver.ResolveContract(objectType).DefaultCreator()(), IDictionary(Of TKey, TValue))))

        For Each pair As KeyValuePair(Of TKey, TValue) In array
            dict.Add(pair.Key, pair.Value)
        Next

        Return dict
    End Function

    Public Overrides Sub WriteJson(writer As JsonWriter, value As Object, serializer As JsonSerializer)
        serializer.Serialize(writer, DirectCast(value, IDictionary(Of TKey, TValue)).Select(Function(p) p))
    End Sub
End Class

And then use it like:

        Dim json As String = "[{""Key"":1,""Value"":""One""},{""Key"":2,""Value"":""Two""}]"

        Dim settings = New JsonSerializerSettings()
        settings.Converters.Add(New KeyValueDictionaryConverter(Of Integer, String)())

        Dim dict = JsonConvert.DeserializeObject(Of Dictionary(Of Integer, String))(json, settings)

Plus code to assert everything was read:

        Dim json2 = JsonConvert.SerializeObject(dict, Newtonsoft.Json.Formatting.Indented, settings)

        If Not JToken.DeepEquals(JToken.Parse(json), JToken.Parse(json2)) Then
            Debug.Assert(JToken.DeepEquals(JToken.Parse(json), JToken.Parse(json2)))
            Throw New ApplicationException("Tokens Not Equal")  
        End If

Working fiddle.

0
votes

This is not the best solution, but it solves my basic need for cross plaftform since client and server codes are done by me, I used Response format as XMl and every function returns only string, but the function returns JSON serialised string.

Dim k As New Dictionary(Of Integer, String)
        k.Add(1, "One One ~ ! @ # $ % ^ & * ( ) _ + |       , . / ; ' [ ] < > ? : ""  { } - = \ / * - + ")
        k.Add(2, "Two")

Return Newtonsoft.Json.JsonConvert.SerializeObject(k, Newtonsoft.Json.Formatting.None)

And on client side I filter for string from { to } and deserialise with json.net. :)

Dim resultstring As String = tk.Result.Substring(tk.Result.IndexOf("{"), tk.Result.LastIndexOf("}") + 1 - tk.Result.IndexOf("{"))

            resultstring = Net.WebUtility.HtmlDecode(resultstring)

            Dim DoWorkResult As Dictionary(Of Integer, String) = Newtonsoft.Json.JsonConvert.DeserializeObject(resultstring, GetType(Dictionary(Of Integer, String)))

The current errors that I am facing would be solving special characters like '&' being sent as "&amp ;" and few others which I solved using HTMLDecode function.

Thank you.