Serialize/Deserialize 2-Dimensional Array

Jan 9, 2009 at 9:47 PM
I have a Json response that comes back with the following (as an example):

{
        "paths" :
        [
          [
            [-109.743767298, 43.2352166520001],
            [-109.883484906, 43.358552074],
            [-109.971837501, 43.3743815290001],
            [-109.997381241, 43.350218419],
          ]
        ]
}

As you can see each item in the array is 2 deep.  To test I've created an object and am attempting to serialize it before attempting to deserialize the actual response.

Here is my object:

    <Serializable()> <JsonObject(MemberSerialization.OptIn)> Public Class PathObj
        Private _paths As Array

        Public Sub New()
            _paths = Array.CreateInstance(GetType(Array), 0)
        End Sub

        Public Sub AddPath(ByVal x As Double, ByVal y As Double)
            'Add the item to the array and resize
            Try
                'Add to paths
                If Not (Me.paths Is Nothing) Then
                    Dim arrSize As Integer = Me.paths.Length + 1

                    'Path
                    Dim path(0, 1) As Double
                    path(0, 0) = x
                    path(0, 1) = y

                    'Resize the array
                    Array.Resize(Me.paths, arrSize)
                    'Add the item to the end of the array
                    Me.paths(arrSize - 1) = path
                End If

            Catch ex As Exception
                'Don't add the item
            End Try
        End Sub

        <JsonProperty()> Public Property paths() As Array()
            Get
                Return _paths
            End Get
            Set(ByVal value As Array())
                _paths = value
            End Set
        End Property

    End Class

Here is the code I am using to deserialize:

      Dim fatt As New PathObj
        fatt.AddPath(1.1, 2.2)
        fatt.AddPath(3.3, 4.4)

        Dim sfatt As String = JsonConvert.SerializeObject(fatt)

And I get the following error:


Server Error in '/gis/testJson' Application.
--------------------------------------------------------------------------------

Array was not a one-dimensional array.
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.ArgumentException: Array was not a one-dimensional array.

Source Error:


Line 38:         fatt.AddPath(3.3, 4.4)
Line 39:
Line 40:         Dim sfatt As String = JsonConvert.SerializeObject(fatt)
Line 41:
Line 42:         TextBox1.Text = sfatt
 

Source File: c:\inetpub\wwwroot\gis\testJson\test\test.aspx.vb    Line: 40

Stack Trace:


[ArgumentException: Array was not a one-dimensional array.]
   System.Array.GetValue(Int32 index) +7658793
   System.Array.System.Collections.IList.get_Item(Int32 index) +4
   Newtonsoft.Json.JsonSerializer.SerializeList(JsonWriter writer, IList values) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:802
   Newtonsoft.Json.JsonSerializer.SerializeValue(JsonWriter writer, Object value, JsonConverter memberConverter) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:654
   Newtonsoft.Json.JsonSerializer.SerializeList(JsonWriter writer, IList values) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:802
   Newtonsoft.Json.JsonSerializer.SerializeValue(JsonWriter writer, Object value, JsonConverter memberConverter) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:654
   Newtonsoft.Json.JsonSerializer.WriteMemberInfoProperty(JsonWriter writer, Object value, JsonMemberMapping memberMapping) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:741
   Newtonsoft.Json.JsonSerializer.SerializeObject(JsonWriter writer, Object value) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:778
   Newtonsoft.Json.JsonSerializer.SerializeValue(JsonWriter writer, Object value, JsonConverter memberConverter) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:674
   Newtonsoft.Json.JsonSerializer.Serialize(JsonWriter jsonWriter, Object value) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonSerializer.cs:624
   Newtonsoft.Json.JsonConvert.SerializeObject(Object value, Formatting formatting, JsonSerializerSettings settings) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonConvert.cs:497
   Newtonsoft.Json.JsonConvert.SerializeObject(Object value) in d:\Newtonsoft\Projects\Json\trunk\Src\Newtonsoft.Json\JsonConvert.cs:447
   test_test.btnSerialize_Click(Object sender, EventArgs e) in c:\inetpub\wwwroot\gis\testJson\test\test.aspx.vb:40
   System.Web.UI.WebControls.Button.OnClick(EventArgs e) +111
   System.Web.UI.WebControls.Button.RaisePostBackEvent(String eventArgument) +110
   System.Web.UI.WebControls.Button.System.Web.UI.IPostBackEventHandler.RaisePostBackEvent(String eventArgument) +10
   System.Web.UI.Page.RaisePostBackEvent(IPostBackEventHandler sourceControl, String eventArgument) +13
   System.Web.UI.Page.RaisePostBackEvent(NameValueCollection postData) +36
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1565

 


--------------------------------------------------------------------------------
Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053



Thanks,
Adam








Coordinator
Jan 14, 2009 at 3:18 AM
Json.NET doesn't support multidimensonal arrays. I had a look at adding support for them and it was a lot of work for little gain. Use a jagged array instead.
Jan 22, 2009 at 7:25 PM
James, will the jagged array deserialize?

Thanks,
Adam
May 1, 2009 at 10:43 PM
Can someone post an example on how to set up a jagged array to get the array objects out of something like this (see the "docs" array, particularly):

{"responseHeader":{"status":0,"QTime":3},"response":{"facet_counts":{},"numFound":17,"docs":[{"Link":"/hello/221192"}],"start":0}}

I can snag everything except what's in the docs array.  The way I'm trying to do this now gives me the following error: "Can not create ListWrapper for type docs."

Here's what I'm trying:
protected void Page_Load(object sender, EventArgs e)
    {
        using (StreamReader reader = new StreamReader(@"c:\json2.txt"))
        {
            string jsonResponse = reader.ReadToEnd();

            Person deserializedPerson = (Person)JsonConvert.DeserializeObject(jsonResponse, typeof(Person));

            Response.Write("QTime: ");
            Response.Write(deserializedPerson.responseHeader.QTime);
            Response.Write("numFound: ");
            Response.Write(deserializedPerson.response.numFound);
            // Response.Write("Link: ");
            // Response.Write(deserializedPerson.response.docs.Link);
        }
}

public class Person
{
    public ResponseHeader responseHeader = new ResponseHeader();
    public Response response = new Response();
}

public class ResponseHeader
{
    public int status, QTime;
}

public class Response
{
    public FacetCounts facet_counts = new FacetCounts();
    public int numFound;
    public docs docs = new docs();
    public string start;
}

public class FacetCounts
{

}

public class docs
{
    string[][] strDocs = new string[1][]
    {
        new string[1]
    };
}

Coordinator
May 2, 2009 at 1:27 AM
That isn't a jagged array, it is an object with a property called Link inside of an array.

I haven't tested this but this should be closer to what you are looking for:

public class Response
{
    public FacetCounts facet_counts = new FacetCounts();
    public int numFound;
    public List<doc> docs;
    public string start;
}

public class doc
{
    public string Link;
}
May 2, 2009 at 6:32 AM
Definitely closer.  Getting a null value for link...I'll keep digging.  Thanks.
May 5, 2009 at 2:46 PM
Okay, your suggestion worked. Thanks. The null value I was receiving was a result of my oversimplification of the input file. The true input file obviously has more than one pair in the array. Here is a more accurate sample input file that works with your suggested changes:

{"responseHeader":{"status":0,"QTime":3},"response":{"facet_counts":{},"numFound":17,"docs":[{"Link":"/hello/221192","ID":"12345"}],"start":0}}
Aug 11, 2010 at 5:34 PM

Hi clemmonsm,

I am working on something similar to what you were working on about a year back. I hope you are still active with these posts.

"resourceSets":  [    {"estimatedTotal":1,"resources":{"name":"1 Microsoft Way, Redmond, WA 98052-300", "bbox":[47.635884282429323,-122.13737419709076,47.643609717570676,-122.12208780290925]},"confidence":"High", "entityType":"Address" ] },

I want to access the objects inside this resourSets Array. This is how I defined the classes. Please check the ResourceSets class. I tried to declare resourceSets as a list of type ResourceSets and at the end, the last code block shows how I am trying to access the object from this list and the arraylist (bbox) within it. The code gives me the error "index out of bound". Could you please tell me how I can work with this list. Any suggestions are welcome.

Thanks,

Lovepreet

 
Public Class TestResult

    Public authenticationResultCode As String, brandLogoUri As String, copyright As String
    'Public resourceSets As New ResourceSets()
    Public resourceSets As List(Of ResourceSets)
    Public statusCode As String, statusDescription As String, traceId As String
    Public address As New PersonAddress()

End Class
Public
Class ResourceSets Public estimatedTotal As Integer Public confidence As String, entityType As String Public resources As New Resources() End Class
Public Class Resources
    Public name As String
    Public bbox As ArrayList
End Class

 Code to access the resourceSet List elements. I am doing all this in VB.Net.

Dim result As TestResult = js.Deserialize(Of TestResult)(jsonResponse)
            SerializedText.Text = "authenticationResultCode: " + result.authenticationResultCode _
                                + " <br /> brandLogoUri: " + result.brandLogoUri _
                                + "<br /> copyright: " + result.copyright _
                                + "<br /> addressline1: " + result.address.addressline1 _
                                + "<br /> addressline2: " + result.address.addressline2 _
                                + "<br /> city: " + result.address.city _
                                + "<br /> pin: " + result.address.pin.ToString _
                                + "<br /> estimatedTotal: " + result.resourceSets(0).estimatedTotal.ToString _
                                + "<br/> name: " + result.resourceSets(1).resources.name _
                                + "<br/> bbox(0): " + result.resourceSets(2).resources.bbox(0).ToString _
                                + "<br/> bbox(1): " + result.resourceSets(2).resources.bbox(1).ToString _
                                + "<br/> confidence: " + result.resourceSets(3).confidence _
                                + "<br/> entityType: " + result.resourceSets(4).entityType _
                                + "<br /> Status Code: " + result.statusCode _
                                + " <br />statusDescrition: " + result.statusDescription _
                                + " <br /> traceId: " + result.traceId
Sep 7, 2010 at 4:32 PM

Hi all,

I finally managed to acess all the elements inside  JSON status file and here is the solution.

First are the classes and then below them is the actual code to access all the elements.

Public Class StatusClass

    Public authenticationResultCode As String, brandLogoUri As String, copyright As String
    Public resourceSets As IList(Of ResourceSets)
    Public statusCode As String, statusDescription As String, traceId As String

End Class

Public Class ResourceSets

    Public estimatedTotal As Integer
    Public resources As IList(Of Resources)

End Class

Public Class Resources

    Public type As Type
    Public id As String
    Public links As IList(Of Links)
    Public completedDate As String, createdDate As String, failedEntityCount As Integer, processedEntityCount As Integer
    Public status As String, totalEntityCount As Integer

End Class

Public Class Links

    Public name As String, role As String, url As String

End Class

 

The code to access the elements

  JobStatusDescriptionLiteral.Text = "authenticationResultCode: " + statusResult.authenticationResultCode _
                                  + " <br /> brandLogoUri: " + statusResult.brandLogoUri _
                                  + "<br /> copyright: " + statusResult.copyright _
                                  + "<br /> Estimated Total: " + statusResult.resourceSets.Item(0).estimatedTotal.ToString _
                                  + " <br />id: " + statusResult.resourceSets.Item(0).resources.Item(0).id _
                                  + " <br />role: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(0).role _
                                  + " <br />url: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(0).url _
                                  + " <br />name: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).name _
                                  + " <br />rol2: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).role _
                                  + " <br />url2: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).url _
                                  + " <br />completedDate: " + statusResult.resourceSets.Item(0).resources.Item(0).completedDate _
                                  + " <br />createdDate: " + statusResult.resourceSets.Item(0).resources.Item(0).createdDate _
                                  + " <br />processedEntityCount: " + statusResult.resourceSets.Item(0).resources.Item(0).processedEntityCount.ToString _
                                   + " <br />failedEntityCount: " + statusResult.resourceSets.Item(0).resources.Item(0).failedEntityCount.ToString _
                                  + " <br />status: " + statusResult.resourceSets.Item(0).resources.Item(0).status _
                                  + " <br />totalEntityCount: " + statusResult.resourceSets.Item(0).resources.Item(0).totalEntityCount.ToString _
                                  + " <br />statusCode: " + statusResult.statusCode _
                                  + " <br />statusDescrition: " + statusResult.statusDescription _
                                  + " <br /> traceId: " + statusResult.traceId

I had to spend days to get this right. I hope this can help save time for others.

Thanks,

Lovepreet


Sep 7, 2010 at 4:43 PM

Hi Again,

In the above solution, I missed a part of the code.

The JSON stream needs to be cleaned before one can access all the elements becuase there is this '_type' element in there which doesn't allow the program to go any further.

Therefore, I first get rid of the '_type' part and then parse the cleaned JSON. The same classes can be used from the above post.

Dim statusURL As String = dataflowJobLocation + "?key=" + key
        'statusURL = statusURL + "?o=xml&key=" + key
        Dim js As New JavaScriptSerializer()
        Dim status As String = "Pending"
        Dim statusResult As StatusClass = Nothing
        Try
            While (status = "Pending")
                Threading.Thread.Sleep(5000)

                Dim requestStatus As HttpWebRequest = DirectCast(WebRequest.Create(statusURL), HttpWebRequest)
                Dim responseStatus As HttpWebResponse = DirectCast(requestStatus.GetResponse(), HttpWebResponse)
                Dim sr As StreamReader = New StreamReader(responseStatus.GetResponseStream())
                Dim jsonResponse As String = sr.ReadToEnd()
                Dim cleanedJson = CleanWebScriptJson(jsonResponse)
                statusResult = js.Deserialize(Of StatusClass)(cleanedJson)

                status = statusResult.resourceSets.Item(0).resources.Item(0).status
                sr.Close()

            End While


            JobStatusDescriptionLiteral.Text = "authenticationResultCode: " + statusResult.authenticationResultCode _
                                  + " <br /> brandLogoUri: " + statusResult.brandLogoUri _
                                  + "<br /> copyright: " + statusResult.copyright _
                                  + "<br /> Estimated Total: " + statusResult.resourceSets.Item(0).estimatedTotal.ToString _
                                  + " <br />id: " + statusResult.resourceSets.Item(0).resources.Item(0).id _
                                  + " <br />role: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(0).role _
                                  + " <br />url: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(0).url _
                                  + " <br />name: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).name _
                                  + " <br />rol2: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).role _
                                  + " <br />url2: " + statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).url _
                                  + " <br />completedDate: " + statusResult.resourceSets.Item(0).resources.Item(0).completedDate _
                                  + " <br />createdDate: " + statusResult.resourceSets.Item(0).resources.Item(0).createdDate _
                                  + " <br />processedEntityCount: " + statusResult.resourceSets.Item(0).resources.Item(0).processedEntityCount.ToString _
                                   + " <br />failedEntityCount: " + statusResult.resourceSets.Item(0).resources.Item(0).failedEntityCount.ToString _
                                  + " <br />status: " + statusResult.resourceSets.Item(0).resources.Item(0).status _
                                  + " <br />totalEntityCount: " + statusResult.resourceSets.Item(0).resources.Item(0).totalEntityCount.ToString _
                                  + " <br />statusCode: " + statusResult.statusCode _
                                  + " <br />statusDescrition: " + statusResult.statusDescription _
                                  + " <br /> traceId: " + statusResult.traceId


            JobStatusLiteral.Text = statusResult.resourceSets.Item(0).resources.Item(0).status

            jobLocationHyperlink.NavigateUrl = statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).url

            Dim successURL As String = statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).url
            jobResultsHyperLink.NavigateUrl = successURL
            'Dim failedURL As String = statusResult.resourceSets.Item(0).resources.Item(0).links.Item(1).url

            'Results(successURL)

            Dim dataFormat As String '= args(1)
            dataFormat = "xml"
            Dim contentType As String = "text/plain"
            If dataFormat.Equals("xml", StringComparison.OrdinalIgnoreCase) Then
                contentType = "application/xml"
            End If

        Catch ex As Exception
            JobStatusLiteral.Text = ex.Message
        End Try

    End Sub

    Private Shared Function CleanWebScriptJson(ByVal json As String) As String
        If String.IsNullOrEmpty(json) Then
            Throw New ArgumentNullException("json")
        End If

        Dim match As Match = RxMsAjaxJsonInner.Match(json)
        Dim innerJson As String = If(match.Success, match.Groups(1).Value, json)
        Return RxMsAjaxJsonInnerType.Replace(innerJson, String.Empty)

    End Function


    Private Shared ReadOnly RxMsAjaxJsonInner As New Regex("^{\s*""d""\s*:(.*)}$", RegexOptions.Compiled)
    Private Shared ReadOnly RxMsAjaxJsonInnerType As New Regex("\s*""__type""\s*:\s*""[^""]*""\s*,\s*", RegexOptions.Compiled)