jquery ajax call return JSON parsing error

2020-02-26 14:55发布

问题:

I am using jquery to call an ajax wcf method that returns a list of objects as a JSON string. The JSON string looks like this when inspecting it in fiddler2 (in TextView):

{"d":"[{\"ID\":\"6b2b8c62-31ce-4df2-982b-054ff5f6be72\",\"Name\":\"Carol\",\"Surname\":\"IrishWife\"},{\"ID\":\"d254740a-0a0f-4a1e-9e4f-0812227dd5af\",\"Name\":\"Willie\",\"Surname\":\"Le Roux\"},{\"ID\":\"660bf0dd-436a-4588-a9c0-19fd6fdcee23\",\"Name\":\"Emmas\",\"Surname\":\"Mum\"},{\"ID\":\"6b9403c5-b728-4e96-bcb1-203e7472eec3\",\"Name\":\"Owen\",\"Surname\":\"Lima\"},{\"ID\":\"d52c08fb-4418-4600-960f-243ff4443ee6\",\"Name\":\"Tim\",\"Surname\":\"Lee\"},{\"ID\":\"e2aacf5b-8855-44ce-9338-3d39f8ab3349\",\"Name\":\"Marcello\",\"Surname\":\"MT\"},{\"ID\":\"578be087-8385-46d6-89de-3db31d352cbc\",\"Name\":\"Carlyn\",\"Surname\":\"Homegroup\"},{\"ID\":\"4c805825-2bee-447a-8b75-41ead17db33e\",\"Name\":\"George\",\"Surname\":\"Homegroup\"},{\"ID\":\"ae48804f-5e78-42c8-9ba0-4214c98a5a89\",\"Name\":\"Isla\",\"Surname\":\"Le Roux\"},{\"ID\":\"f8be2f4f-fedb-4863-8a84-44fddea84ea9\",\"Name\":\"Peter\",\"Surname\":\"Anderson\"},{\"ID\":\"15e7644d-ec43-44ff-a959-47e00112da6b\",\"Name\":\"Kitty\",\"Surname\":\"Corbett\"},{\"ID\":\"8fd7fccc-335c-4d5c-93b5-4b00f96a9950\",\"Name\":\"Natalie\",\"Surname\":\"Archibald\"},{\"ID\":\"09b5aad2-2cf1-488a-962b-4d692b05ddea\",\"Name\":\"Miku\",\"Surname\":\"Heally\"},{\"ID\":\"affa369e-5af3-4537-a0f4-71422956da41\",\"Name\":\"Steven\",\"Surname\":\"Corbett\"},{\"ID\":\"65f57da3-4f88-4798-9590-83b4ccecfc44\",\"Name\":\"Tim\",\"Surname\":\"Archibald\"},{\"ID\":\"53bfb451-f66f-4b6e-b430-8d13c95b30d8\",\"Name\":\"Philip\",\"Surname\":\"MT\"},{\"ID\":\"c7f22b9b-4030-4f82-9f75-bbb726cabb73\",\"Name\":\"Vincent\",\"Surname\":\"Van Der Walt\"},{\"ID\":\"232577be-3165-4316-a20d-c2f2a09c5382\",\"Name\":\"Scott\",\"Surname\":\"Lynn\"},{\"ID\":\"913508a1-5dca-4504-8caf-c8e3dc386fc0\",\"Name\":\"Dan\",\"Surname\":\"MT\"},{\"ID\":\"36054a07-b14d-4c1c-b35f-e00875dde7e5\",\"Name\":\"Sarah\",\"Surname\":\"MT\"},{\"ID\":\"f14e7d98-e040-4ba9-928f-f2ff48116b0b\",\"Name\":\"Josh\",\"Surname\":\"IrishDude\"}]"}

When I inspect the result in fiddler's JSON View, it shows the following JSON:

d=[{"ID":"6b2b8c62-31ce-4df2-982b-054ff5f6be72","Name":"Carol","Surname":"IrishWife"},{"ID":"d254740a-0a0f-4a1e-9e4f-0812227dd5af","Name":"Willie","Surname":"Le Roux"},{"ID":"660bf0dd-436a-4588-a9c0-19fd6fdcee23","Name":"Emmas","Surname":"Mum"},{"ID":"6b9403c5-b728-4e96-bcb1-203e7472eec3","Name":"Owen","Surname":"Lima"},{"ID":"d52c08fb-4418-4600-960f-243ff4443ee6","Name":"Tim","Surname":"Lee"},{"ID":"e2aacf5b-8855-44ce-9338-3d39f8ab3349","Name":"Marcello","Surname":"MT"},{"ID":"578be087-8385-46d6-89de-3db31d352cbc","Name":"Carlyn","Surname":"Homegroup"},{"ID":"4c805825-2bee-447a-8b75-41ead17db33e","Name":"George","Surname":"Homegroup"},{"ID":"ae48804f-5e78-42c8-9ba0-4214c98a5a89","Name":"Isla","Surname":"Le Roux"},{"ID":"f8be2f4f-fedb-4863-8a84-44fddea84ea9","Name":"Peter","Surname":"Anderson"},{"ID":"15e7644d-ec43-44ff-a959-47e00112da6b","Name":"Kitty","Surname":"Corbett"},{"ID":"8fd7fccc-335c-4d5c-93b5-4b00f96a9950","Name":"Natalie","Surname":"Archibald"},{"ID":"09b5aad2-2cf1-488a-962b-4d692b05ddea","Name":"Miku","Surname":"Heally"},{"ID":"affa369e-5af3-4537-a0f4-71422956da41","Name":"Steven","Surname":"Corbett"},{"ID":"65f57da3-4f88-4798-9590-83b4ccecfc44","Name":"Tim","Surname":"Archibald"},{"ID":"53bfb451-f66f-4b6e-b430-8d13c95b30d8","Name":"Philip","Surname":"MT"},{"ID":"c7f22b9b-4030-4f82-9f75-bbb726cabb73","Name":"Vincent","Surname":"Van Der Walt"},{"ID":"232577be-3165-4316-a20d-c2f2a09c5382","Name":"Scott","Surname":"Lynn"},{"ID":"913508a1-5dca-4504-8caf-c8e3dc386fc0","Name":"Dan","Surname":"MT"},{"ID":"36054a07-b14d-4c1c-b35f-e00875dde7e5","Name":"Sarah","Surname":"MT"},{"ID":"f14e7d98-e040-4ba9-928f-f2ff48116b0b","Name":"Josh","Surname":"IrishDude"}]

So fiddler can parse it successfully, but on the client, the jquery ajax error callback function displays the following error:

Error: No conversion from text to application/json

The wcf method is defined as follows:

    [OperationContract]
    [WebGet(ResponseFormat=WebMessageFormat.Json)]
    public string GetPeople(Guid groupId)
    {
        using (SchedulerContext context = new SchedulerContext())
        {
            JavaScriptSerializer ser = new JavaScriptSerializer();

            var query = from p in context.People
                        where p.Group_ID == groupId
                        select new
                        {
                            p.ID,
                            p.Name,
                            p.Surname
                        };

            return ser.Serialize(query.ToArray());
        }   
    }

And finally, the calling jquery is:

$.ajax(
        {
            type: "GET",
            dataType: "application/json",
            contentType: "json",
            data: { groupId: 'ae09a080-5d7c-4e92-9a87-591574b7c4b8' },
            url: "WebAPI.svc/GetPeople",
            error: function (jqXHR, textStatus, errorThrown) {
                alert("error");
            },
            success: function (msg) {
                alert(msg.d[0].Name);
            }
        }
);

Thanks in advance!

UPDATE: Thanks to @user1370958, one step closer to the solution.

When changing the error callback function to the following it successfully returns the result...

error: function (jqXHR, textStatus, errorThrown) {
    var test = $.parseJSON(jqXHR.responseText);
    var test2 = $.parseJSON(test.d);
    alert(test2[0].Name);
},

Not sure why, but I have to parse the result and then parse the nested objects inside that. I'm assuming if any of my returned types contained complex objects it would also have needed another parse...

回答1:

Here, "application/json" is not a valid value for the dataType property. I changed it to "json" in my project and the same problem was solved.

Please check details here (comment #7): http://bugs.jquery.com/ticket/8216



回答2:

Try adding the MIME type in your server-side code:

Response.ContentType = "application/json";


回答3:

With WCF 4.0, you can add an attribute called automaticFormatSelectionEnabled which allows the service to look at the Accept header in the HTTP request to determine what format to return. As long as what you are returning is serializable, WCF will handle the correct serialization for you. In your jQuery ajax call, the Accept header is added by including accepts: {json: "application/json"}.



回答4:

I assume you want to return the value of ser.Serialize(query.ToArray()) to the client (an array). But you're returning it as a string, so WCF will escape that JSON into a string, and what you'll end up is not an array, but a string.

Since you're using anonymous types, which aren't natively supported by WCF, you need to use the JavaScriptSerializer. So to prevent the double-encoding of the JSON (into the string) you should return the data as a Stream instead, so that WCF won't touch your data (see sample code below).

One more thing: I see your response has a {"d":...} wrapper, which suggests that you're using the <enableWebScript/> / WebScriptEnablingBehavior / WebScriptServiceHostFactory when defining your service / endpoint. Since you're not using the ASP.NET AJAX library, you don't need that wrapping, so you can use the "simpler" <webHttp/> / WebHttpBehavior / WebServiceHostFactory instead, and your response won't be wrapped in that "d" object.

[OperationContract]
[WebGet(ResponseFormat = WebMessageFormat.Json)]
public System.IO.Stream GetPeople(Guid groupId)
{
    using (SchedulerContext context = new SchedulerContext())
    {
        JavaScriptSerializer ser = new JavaScriptSerializer();

        var query = from p in context.People
                    where p.Group_ID == groupId
                    select new
                    {
                        p.ID,
                        p.Name,
                        p.Surname
                    };

        string json = ser.Serialize(query.ToArray());
        using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(json)))
        {
            WebOperationContext.Current.OutgoingResponse.ContentType = "application/json; charset=utf-8";
            return ms;
        }

}


回答5:

I've found a workaround:

The first problem was the circular reference exception on the entity model. To overcome this I use the following code to Detach my entities from the context and then serialize them to strings. I then serialize them on the client using the code below that.

Service

    [WebGet(ResponseFormat = WebMessageFormat.Json)]
    [OperationContract]
    public string[] GetPeople(Guid groupId)
    {
        using (SchedulerContext context = new SchedulerContext())
        {

            var people = (from p in context.People
                          where p.Group_ID == groupId
                          select p).ToList();

            JavaScriptSerializer ser = new JavaScriptSerializer();
            string[] result = new string[people.Count];
            for (int i = 0; i<people.Count; i++)
            {
                context.Detach(people[i]);
                string json = ser.Serialize(people[i]);
                result[i] = json;
            }
            return result;
        }   
    }

Client

        $.ajax(
                {
                    type: "GET",
                    //dataType: "application/json",
                    //dataType: "text/plain",
                    contentType: "json",
                    data: { groupId: 'ae09a080-5d7c-4e92-9a87-591574b7c4b8' },
                    //data: { groupId: 'test' },
                    //data: { groupId: '739526F1-7C58-4E3B-97D8-4870948BFE32' },
                    url: "WebAPI.svc/GetPeople",
                    error: function (jqXHR, textStatus, errorThrown) {
                        alert(jqXHR.resultText);
                    },
                    success: function (people) {
                        //the returned param "people" is of type string[], so each string needs parsed 
                        $(people).each(function (index, value) {
                            var person = $.parseJSON(value);
                            //now I can use the Person object
                        });

                    }
                }

        );