Fabian Michel
Fabian Michel

Reputation: 35

Deserialize Json to search for a String

My Program has a TextBox and a Button. I want to be able to enter text (the id, for example "1000") into that textbox, and when I Press the button it searches in a Json String for all the entries with the ID "1000" and outputs the description of it. (there are multiple entries with the same id 1000 so i need a collection).

But using my Code I always get the Error:

Cannot deserialize the current JSON object (e.g. {"name":"value"}) into type 'System.Collections.Generic.List`1[MyProgram.MyObject2]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly. To fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object. Path '1000', line 1, position 8."

I have a json string that looks something like this:

{
   "1000": {
      "id": 1000,
      "description":"my description 1000"
    },
    "1001": {
       "id": 1001,
       "description":"my description 1001"
    },
    ...
}

I have the class:

Public Class MyObject2
   Public Property id As Integer
   Public Property description As String
End Class

Now I try to deserialize it with:

 Dim objectList = JsonConvert.DeserializeObject(Of List(Of MyObject2))(result)

To add all objects from the js string to an object list i use:

Dim foundItem = objectList.Where(Function(__) __.name.Contains(x))

Upvotes: 0

Views: 308

Answers (2)

David
David

Reputation: 6131

Visual Studio has a cool feature called Paste JSON as Classes that can be found under Edit > Paste Special > Paste JSON as Classes. If you think of your JSON as a Dictionary(Of String, CustomClass) then using the Paste JSON as Classes using the class information.

When I do that, I get the following output:

Public Class Rootobject
    Public Property id As Integer
    Public Property description As String
End Class

Using Newtonsoft, you can clean it up a little bit using decorators:

Public Class RootObject

    <JsonProperty("id")>
    Public Property Id As Integer

    <JsonProperty("description")>
    Public Property Description As String

End Class

Now that the class is setup, you can use the DeserializeObject method (documentation) to deserialize the JSON into a Dictionary(Of String, RootObject):

Dim dictionary = JsonConvert.DeserializeObject(Of Dictionary(Of String, RootObject))(json)

Once you have the Dictionary setup, you can use the ContainsKey method (documentation) to check if the entered value is in the collection and if the result returns a value then you can get the class:

Private ReadOnly _dictionary As Dictionary(Of String, RootObject)
Sub New()
    InitializeComponent()

    ' this example assumes you have the JSON literal stored in a variable named "json"
    _dictionary = JsonConvert.DeserializeObject(Of Dictionary(Of String, RootObject))(json)
End Sub

Private Sub ButtonSearch_Click(sender As Object, e As EventArgs) Handles ButtonSearch.Click
    Dim key = TextBoxSearch.Text
    If (_dictionary.ContainsKey(key)) Then
        Dim result = _dictionary(key)
        ' do something with result.Id or result.Description
    End If
End Sub

Example

https://dotnetfiddle.net/XmplKB

Upvotes: 3

Jimi
Jimi

Reputation: 32288

The message is informing that the Type you asked to deserialize the JSON to, is not representing the JSON structure correctly:

Cannot deserialize the current JSON object [...] into type [List] because the type requires a JSON array to deserialize correctly [...]

And suggests to either modify the JSON to an Array or change the Type it deserializes to.
You of course don't want to change the JSON.

The JSON you posted here is enclosed in braces, so it represents an Object:

{ 
    { [...] },
    { [...] }
}

To deserialize to a an Array or List, it should be enclosed in square brackets:

[ 
    { [...] },
    { [...] }
]

This object contains a sequence of indexed objects. It's common to deserialize this kind of structure to a Dictionary(Of [Type], [Object]) (as long as the indexes are unique); using a class model is not really practical (or feasible), since these indexes can vary in number and form.

An indexed object here is defined as:

"1000": {
   "id": 1000,
   "description":"my description 1000"
 }

You can choose to deserialize the index to either a string or an integer Type (Integer, Long): in this case, the conversion is automatic, there's nothing else to do.
The Object is defined by your MyObject2 Type.
So, the Dictionary(Of [Type], [Object]) can be a Dictionary(Of String, MyObject2).
You probably prefer to keep the index as a string, since you want to use a TextBox to input the index of the object to look for.

The deserialization method is then changed in:

Dim myObjects = JsonConvert.DeserializeObject(Of Dictionary(Of String, MyObject2))(result) 

To find the description property of an object using its index, you can then just use the Key of your Dictionary:

Dim inputKey = SomeTextBox.Text

' [...]

Dim obj As MyObject2 = Nothing

If myObjects.TryGetValue(inputKey, obj) Then
    Return obj.description
Else
    ' Notify that the Index provided doesn't have a matching value
End If

Upvotes: 1

Related Questions