0

I try to draw tree with d3 from here.

When I try to parse it get errror: Uncaught SyntaxError: Unexpected token u in JSON at position 0 at JSON.parse () at Test:44

Code here:

function getTree() {
        $.ajax({
            url: "/Test/FPTree",
            dataType: 'json',
            success: function (data) {
                return data;
            }
        })
    }
    var treeData = [];

    treeData.push(JSON.parse(getTree()));

    var margin = { top: 20, right: 120, bottom: 20, left: 120 },
        width = 960 - margin.right - margin.left,
        height = 500 - margin.top - margin.bottom;

    var i = 0;

    var tree = d3.layout.tree()
        .size([height, width]);

    var diagonal = d3.svg.diagonal()
        .projection(function (d) { return [d.y, d.x]; });

    var svg = d3.select("body").append("svg")
        .attr("width", width + margin.right + margin.left)
        .attr("height", height + margin.top + margin.bottom)
        .append("g")
        .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    root = treeData[0];

    update(root);

    function update(source) {

        var nodes = tree.nodes(root).reverse(),
            links = tree.links(nodes);

        nodes.forEach(function (d) { d.y = d.depth * 180; });

        var node = svg.selectAll("g.node")
            .data(nodes, function (d) { return d.id || (d.id = ++i); });

        var nodeEnter = node.enter().append("g")
            .attr("class", "node")
            .attr("transform", function (d) {
                return "translate(" + d.y + "," + d.x + ")";
            });

        nodeEnter.append("circle")
            .attr("r", 10)
            .style("fill", "#fff");

        nodeEnter.append("text")
            .attr("x", function (d) {
                return d.children || d._children ? -13 : 13;
            })
            .attr("dy", ".35em")
            .attr("text-anchor", function (d) {
                return d.children || d._children ? "end" : "start";
            })
            .text(function (d) { return d.name; })
            .style("fill-opacity", 1);

        var link = svg.selectAll("path.link")
            .data(links, function (d) { return d.target.id; });

        link.enter().insert("path", "g")
            .attr("class", "link")
            .attr("d", diagonal);
    }

And this is my JSON from server (Use Postman to get it) its valid.:

{
  "name": null,
  "parent": null,
  "children": [
    {
      "name": "A",
      "parent": null,
      "children": [
        {
          "name": "B",
          "parent": "A",
          "children": [
            {
              "name": "C",
              "parent": "B",
              "children": []
            }
          ]
        }
      ]
    }
  ]
}

And code from controller's action

 [HttpGet]
    public ContentResult FPTree()
    {
        FPTree Tree = new FPTree();
        Tree.Add(new List<string> { "A", "B", "C" });

        var settings = new JsonSerializerSettings
        {
            Converters = new List<JsonConverter> { new FPTreeConverter() },
            Formatting = Formatting.Indented
        };
        return Content(JsonConvert.SerializeObject(Tree.root, settings), "application/json");
    }
  • your JSON is 'undefined' bc you aren't waiting for the result to come back before calling JSON.parse – Cruiser Apr 11 '17 at 17:29
  • `getTree` is calling an `ajax` function and is returning nothing and hence your `json.parse` fails. What you have to do is to wrap all logic that needs the data inside a function and call it from `ajax` `success` after your receive the data – Panther Apr 11 '17 at 17:29

2 Answers2

0

You're calling the function and "using" the data before it's actually retrieved.

You should use the data that's returned by the promise inside the callback function that you call whenever the AJAX suceeds, not outside of it.

In other words: You call getTree(), therefore launching the AJAX request, and you try to parse the data that it returns immediately after calling it. However, the AJAX request takes some time (this depends on many things, but it's usually under a second) to process and return the data, since it's asynchronous. When the success callback is called, your other function has already stopped because the return data from the getTree() is undefined (Hence the "u" in the parse error you get).

Try something like this:

    var tree = d3.layout.tree().size([height, width]);

    function getTree() {
        $.ajax({
            url: "/Test/FPTree",
            dataType: 'json',
            success: function (data) {

                var treeData = [];
                treeData.push(JSON.parse(data));

                var margin = { top: 20, right: 120, bottom: 20, left: 120 },
                    width = 960 - margin.right - margin.left,
                    height = 500 - margin.top - margin.bottom;
                var i = 0;

                var diagonal = d3.svg.diagonal()
                    .projection(function (d) { return [d.y, d.x]; });
                var svg = d3.select("body").append("svg")
                    .attr("width", width + margin.right + margin.left)
                    .attr("height", height + margin.top + margin.bottom)
                    .append("g")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");
                root = treeData[0];
                update(root);
            }
        })
    }

    function update(source) {
        var nodes = tree.nodes(root).reverse(),
            links = tree.links(nodes);

        nodes.forEach(function (d) { d.y = d.depth * 180; });

        var node = svg.selectAll("g.node")
            .data(nodes, function (d) { return d.id || (d.id = ++i); });

        var nodeEnter = node.enter().append("g")
            .attr("class", "node")
            .attr("transform", function (d) {
                return "translate(" + d.y + "," + d.x + ")";
            });

        nodeEnter.append("circle")
            .attr("r", 10)
            .style("fill", "#fff");

        nodeEnter.append("text")
            .attr("x", function (d) {
                return d.children || d._children ? -13 : 13;
            })
            .attr("dy", ".35em")
            .attr("text-anchor", function (d) {
                return d.children || d._children ? "end" : "start";
            })
            .text(function (d) { return d.name; })
            .style("fill-opacity", 1);

        var link = svg.selectAll("path.link")
            .data(links, function (d) { return d.target.id; });

        link.enter().insert("path", "g")
            .attr("class", "link")
            .attr("d", diagonal);
    }
FireSarge
  • 913
  • 8
  • 19
0

Looks like in your getTree() function isn't actually returning anything. It's returning undefined which is why you're getting that error message. The problem is in the success function in your ajax call, you're returning data, but you're only returning in the anonymous function you passed to the success parameter. The getTree() function actually returns right away because the ajax call is asynchronous. Instead you should be using callback functions. That is, pass a function into the getTree() function that will be called with the data when it is done. Here's an example.

function getTree(callback) {
    $.ajax({
        url: "/Test/FPTree",
        dataType: 'json',
        success: function (data) {
            callback(data);
        }
    })
}

getTree(function(data) {

    var treeData = [];

    treeData.push(JSON.parse(getTree()));

    var margin = { top: 20, right: 120, bottom: 20, left: 120 },
    width = 960 - margin.right - margin.left,
    height = 500 - margin.top - margin.bottom;

    var i = 0;

    var tree = d3.layout.tree()
    .size([height, width]);

    var diagonal = d3.svg.diagonal()
    .projection(function (d) { return [d.y, d.x]; });

    var svg = d3.select("body").append("svg")
    .attr("width", width + margin.right + margin.left)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    root = treeData[0]

    update(root)
});
Francesco
  • 99
  • 1