Render D3-график из строки JSON вместо файла JSON

Я пытаюсь сделать следующий Dendrogram из моего приложения Rails: http://bl.ocks.org/mbostock/4063570

У меня есть модель со многими атрибутами, но я хотел бы вручную вложить эти атрибуты и просто использовать строчную интерполяцию для создания моей собственной строки JSON, а затем передать ее непосредственно на d3.

Вот мой код:

    <%= javascript_tag do %>
        var width = 960,
        height = 2200;

        var cluster = d3.layout.cluster()
        .size([height, width - 160]);

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

        var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height)
        .append("g")
        .attr("transform", "translate(40,0)");

        **d3.json("/assets/flare.json", function(root) {**
        var nodes = cluster.nodes(root),
        links = cluster.links(nodes);

        var link = svg.selectAll(".link")
        .data(links)
        .enter().append("path")
        .attr("class", "link")
        .attr("d", diagonal);

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

        node.append("circle")
        .attr("r", 4.5);

        node.append("text")
        .attr("dx", function(d) { return d.children ? -8 : 8; })
        .attr("dy", 3)
        .style("text-anchor", function(d) { return d.children ? "end" : "start"; })
        .text(function(d) { return d.name; });
        });

        d3.select(self.frameElement).style("height", height + "px");
    <% end %>

Вот моя (неустановленная) строка JSON:

var mystring = '{
    "name": "Product",
    "properties": {
        "id": {
            "type": "number",
            "description": "Product identifier",
            "required": true
        },
        "name": {
            "type": "string",
            "description": "Name of the product",
            "required": true
        },
        "price": {
            "type": "number",
            "minimum": 0,
            "required": true
        },
        "tags": {
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "stock": {
            "type": "object",
            "properties": {
                "warehouse": {
                    "type": "number"
                },
                "retail": {
                    "type": "number"
                }
            }
        }
    }
}';

Вещи, которые я пробовал:

1), минимизируя JSON, поэтому он вводится как одна строка (без эффекта)

2) запуск JSON.parse(mystring) в строке

3), просматривая документацию D3 и и googling, чтобы изменить следующую функцию, чтобы принять строку вместо пути к файлу:   d3.json( "/assets/flare.json", function (root) {           var nodes = cluster.nodes(root),           links = cluster.links(узлы);

Ответ 1

Сначала давайте посмотрим, что делает d3.json.

d3.json("/assets/flare.json", function(root) {
    // code that uses the object 'root'
});

Загружает файл /assets/flare.json с сервера, интерпретирует содержимое как JSON и передает полученный объект в качестве аргумента root анонимной функции.

Если у вас уже есть объект JSON, вам не нужно использовать функцию d3.json - вы можете просто использовать объект напрямую.

var root = {
   "name": "flare",
   "children": [
     ...
   ]
};
// code that uses the object 'root'

Если объект представлен в виде строки, вы можете использовать JSON.parse для получения объекта:

var myString = '{"name": "flare","children": [ ... ] }';
var root = JSON.parse(mystring);
// code that uses the object 'root'

Во-вторых, давайте посмотрим, что d3.layout.cluster ожидает ваших данных. По документам:

... дочерний аксессуар по умолчанию предполагает, что каждый входной объект является объектом с дочерним массивом...

Другими словами, данные должны иметь форму:

var mystring = '{
    "name": "Product",
    "children": [
        {
            "name": "id",
            "type": "number",
            "description": "Product identifier",
            "required": true
        },
        ...
        {
            "name": "stock",
            "type": "object",
            "children": [
                {
                    "name: "warehouse",
                    "type": "number"
                },
                {
                    "name": "retail",
                    "type": "number"
                }
            ]
        }
    ]
}

Ответ 2

d3.json фактически использует URL как аргумент, поэтому вместо того, чтобы указывать путь к файлу, я бы предложил делегировать управление данными контроллеру (особенно, если в будущем вам нужно будет загрузить его из БД) поэтому для упрощения вещей:

  • Создайте в контроллере метод, который фактически откроет файл и вернет его содержимое:
class YourFlareController < ApplicationController
    def load
        @data = File.read("app/assets/json/flare.json")
        render :json => @data
    end
end
  • Убедитесь, что у вас есть маршрут на ваших маршрутах. rb

получить "yourflare/load"

  • И теперь в вашем javascript вы можете просто вызвать

d3.json("http://host/yourflare/load", function(root) {