4

I have been playing with jstree (1.0rc2)+jquery (1.4.2) for the first time with c#.net and although I have gotten it working, there are a couple things that I don't understand about how data is provided to the tree by the webservice I use to populate the tree (using ajax and the json_data plug-in). I was hoping someone with more experience using jstree could provide some insight.

The jstree config looks like this:

 "json_data": {
                "ajax": {
                    "url": "GetTree.asmx/GetChildren",
                    "type": "POST",
                    "contentType": "application/json; charset=utf-8",
                    "dataType": "json",
                    "data": function(n) {
                        var result = "{'id':'" + (n.attr ? n.attr("id").replace("node_", "") : "0") + "'}";
                        return (result);
                    }
                }
            }

GetTree.asmx GetChildren method:

   [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Xml )]
    public string GetChildren(string id)
    {
        List<jsTreeNode> jsTree = new List<jsTreeNode>();
        //... (build the tree as needed)

        JavaScriptSerializer serializer = new JavaScriptSerializer();
        return(serializer.Serialize(jsTree)); 
    }

Question 1: So everything works great, so what's the problem? The problem is "ResponseFormat = ResponseFormat.Xml". I struggled for a while to get this working because it did not work when it was set to ResponseFormat.Json, which is what I would expect it to be. In that situation, no errors would be reported by the web service or by jQuery when parsing the json response, but the tree would render empty.

In looking at the HTML output of the web service, I could see no difference between what was rendered either way. I was hoping someone could explain why this works (counterintuitively) and why it does not work with ResponseFormat.Json, and if this is indicative of something else I might be doing wrong.

Question 2: Generally, web service or web handler?

Would using a generic web handler (ashx) be a more efficient way to do this anyway? Is there a substantial difference in the overhead required of a standard web service versus a generic web handler? Since my goal is basically to control exactly what is output (and using the json data format in the web service doesn't seem to be working the way I want it to anyway) I am not sure what benefit, if any, there is to using a web service here instead of just stripping it down completely. On the other hand this works now so maybe I should leave well enough alone.

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Jamie Treworgy
  • 23,934
  • 8
  • 76
  • 119

3 Answers3

4

Seeing as this question has almost 600 views and no answers I thought I would answer it myself (since I've long since figured it out).

Using a ScriptMethod is really not the right way to communicate with jQuery ajax. While it can be done, you will notice what I was doing above is returning a string with data that I encoded myself to JSON using JavascriptSerializer.

However, using a ScriptMethod automatically incorporates serialization/deserialization designed to communicate with Microsoft's AJAX framework. Since serializing a pure string with no object wrapper will genererally result in the same string (whether I be returning XML or JSON format), it basically worked, but what was really happening internally is it was being serialized twice.

So what I should have done, at a minimum, was:

public List<jsTreeNode> GetChildren(string id)

that is, the return type should be the actual data type, not a string of serialized data.

However, this still wouldn't be exactly right, because Microsoft's methods wrap the return value in an object d. I could still extract that in Javascript to get the inner data. But if something like jsTree is expecting data in a predefined format this may not be workable.

The best solution is do not use WebServices, use generic handlers (ashx) instead. This gives you complete control over the format and handling of your input and output. It may take a little bit of doing to set yourself up a nice framework, but the frustration of being unable to skip parts of the WebService handling that you don't need makes it well worth it.

Jamie Treworgy
  • 23,934
  • 8
  • 76
  • 119
  • Glad to see you figured this out... I had a similar problem and came to the conclusion a long time ago. Had I seen this, I would have helped. – jlrolin Apr 26 '11 at 15:51
1

Sorry, I have to disagree with your answer the 3.5 framework has really good support for Json serialization and Web Services (and for 2.0 you can use Newtonsoft.Json). Please see my JsTree ASP.NET Web Control Demo at http://code.zyky.com/jsTreeView/Default.aspx and http://asp-net-elephant.blogspot.com/2012/01/how-to-use-jstree-in-aspnet-web-forms.html for an example of both. Hope this helps.

  • Your example looks great! Where were you in October '10 when I was a babe in the woods?? :) With all the hindsight I have now, though, I definitely wouldn't advise someone start a new project using web services. There's just better stuff now, like WCF, or use `System.Web.Routing` and roll your own slick REST framework, e.g. http://blogs.msdn.com/b/henrikn/archive/2012/02/23/using-asp-net-web-api-with-asp-net-web-forms.aspx – Jamie Treworgy Mar 08 '12 at 22:59
0

Regarding Question 1 (I cannot speak to Q2), I have gotten a web service to feed JSON back to the jsTree plugin. While I recognize that WCF and REST are more current approaches, and no doubt better in the long run, we still use asmx web services and they do get the job done. But it was not easy: I spent a while trying to get the exact syntax to work in JS so that jsTree would get its data object from an ASP.NET web service. As the OP alludes to in his solution, the problem isn't so much my JS and wiring of the plugin but was with the wonky JSON data that was returned my web service. It -looked- like JSON but it was a string representation of a simplified JSON object.

To fix it I had to deserialize and serialize my json string (I got a clue about this from https://stackoverflow.com/a/20080495/1129926) and the resulting object was consumed happily by jsTree. Here's my web service:

Imports System.Web.Script.Serialization

Public Function GetJsonData() As String
    Dim jss As JavaScriptSerializer = New JavaScriptSerializer
    ' IMPORTANT: do not use single quotes ' in JSON string, use ""
    Dim jsonData As String = "" & _
    "{ ""text"": ""CONTACTS"", ""children"": [ { ""text"": ""CUSTOMER"", ""column"": ""CLASS"", ""children"": [ { ""text"": ""Excelo Communications"", ""column"": ""COMPANY"", ""children"": [{ ""text"": ""Fred Shorts"", ""column"": ""CONTACT"" }] } ] }, { ""text"": ""USER"", ""column"": ""CLASS"" } ] }"
    Dim o As Object = Nothing
    Try
        ' deserialize the JSON into an Object,
        ' shout out to: https://stackoverflow.com/a/20080495/1129926
        o = jss.Deserialize(Of Object)(jsonData)
        o = jss.Serialize(o)
        Context.Response.Clear()
        Context.Response.ContentType = "application/json; charset=utf-8"
    Catch ex As Exception
        // log something
    End Try

    Return o

End Function

On the client I initialized jsTree in script block as follows:

$(document).ready(function () {
    var sURL = "../dataIO.asmx/GetJsonData";
    var dataIn = "";
    $.ajax({
        async: true,
        type: "POST",
        url: sURL,
        dataType: "json",
        data: dataIn,
        contentType: "application/json; charset=utf-8",
        success: function (data) {
            console.log("data obj:" + data);
            createJSTrees(data);
        },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
            console.log(XMLHttpRequest.statusText + "(status=" + XMLHttpRequest.status + "): " + XMLHttpRequest.responseText);
        }
    });
});
function createJSTrees(jsonData) {
    $("#jstree_dataNav").jstree({
        "core": {
            "data": jsonData
        }
    });
    $("#jstree_dataNav").on("changed.jstree", function (e, data) {
        console.log(data.selected);
    });
}

<div id="jstree_dataNav"></div>

jsTree has a somewhat alternative syntax whereas you call the web service within the core.data section but I was unable to get that to work. Instead, I call my web service via ajax and then pass the JSON data object to a function that initializes the jsTree plugin and jsTree just used the object passed in within data:.

Community
  • 1
  • 1
Jeff Mergler
  • 1,384
  • 20
  • 27