最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - Getting nested XML data with D3 - Stack Overflow

programmeradmin0浏览0评论

My excel file, called thefilehere.xml contains the following information:

<Main>
  <Name>Rotunda</Name>
  <Age>43</Age>
</Main>
<Main>
  <Name>John</Name>
  <Age>22</Age>
</Main>

As someone who has litle experience with D3 and XML files, I am trying to understand how to call the nested data from my XML file. My basic HTML is as follows:

HTML

<div id="chart"></div> 

My Javascript, which does not work, hopefully makes it clear what I am trying to acplish. I want to select the main container (in this case Main), and from there select its children (in this case Name and Age). Age is to be shown via graph. Name is to be the label on the graph.

JavaScript

  d3.xml("thefilehere.xml", function(err, xml) {
    d3.select("#chart")
    .selectAll("div")
    .data(xml.documentElement.getElementsByTagName("Main"))
    .enter().append("div")
    .style("width", function(d) { return d("Age").textContent * 1 + "px"; })
    .text(d("Name").textContent);
  });

My excel file, called thefilehere.xml contains the following information:

<Main>
  <Name>Rotunda</Name>
  <Age>43</Age>
</Main>
<Main>
  <Name>John</Name>
  <Age>22</Age>
</Main>

As someone who has litle experience with D3 and XML files, I am trying to understand how to call the nested data from my XML file. My basic HTML is as follows:

HTML

<div id="chart"></div> 

My Javascript, which does not work, hopefully makes it clear what I am trying to acplish. I want to select the main container (in this case Main), and from there select its children (in this case Name and Age). Age is to be shown via graph. Name is to be the label on the graph.

JavaScript

  d3.xml("thefilehere.xml", function(err, xml) {
    d3.select("#chart")
    .selectAll("div")
    .data(xml.documentElement.getElementsByTagName("Main"))
    .enter().append("div")
    .style("width", function(d) { return d("Age").textContent * 1 + "px"; })
    .text(d("Name").textContent);
  });
Share Improve this question asked Apr 20, 2017 at 16:11 Jason ChenJason Chen 2,5876 gold badges27 silver badges47 bronze badges
Add a ment  | 

3 Answers 3

Reset to default 5

This answer adds to and builds upon the work already done by @GerardoFurtado in his answer which is perfectly fine and well-suited for many situations. However, as it is often times desirable to stay within D3 to not break its basic operating concepts, here is my D3-only—mostly, at least— solution.

Since D3 is not restricted to handling SVG DOM trees, but is capable of handling—amongst others—XML DOM just as well, you can easily use its selections on the loaded XML file. The relevant code might look like this:

d3.xml(url, (error, xml) => {
  let xmlDoc = d3.select(xml.documentElement);   // wrap the XML document in a D3 selection

  d3.select("body")
    .selectAll("div")
    .data(xmlDoc.selectAll("Main").nodes())      // bind the XML's nodes as data
    .enter().append("div")
      .attr("class", "myDiv")
      .style("width", d => d3.select(d).select("Age").text() * 5 + "px")
      .text(d => d3.select(d).select("Name").text());
});

When doing d3.select(d) within .style() and .text() it is important to notice, that this will actually select from the XML's DOM rather than from the HTML's DOM. Since we bound the selected elements from xmlDoc.selectAll("Main") as data, d in this case will refer to a <Main> element of the XML. Having this selection at hand, you can leverage D3's capabilities by doing sub-selections as well as accessing its contents by calling .text() and much more, if needed.

Have a look at the following snippet for a working demo:

// Set up data to simulate loading from file.
// This can be ignored for the reasoning of this answer; main logic below.
const url = URL.createObjectURL(new Blob([
  `<data>
     <Main>
       <Name>Rotunda</Name>
       <Age>43</Age>
     </Main>
     <Main>
       <Name>John</Name>
       <Age>22</Age>
     </Main>
   </data>`
]));
// End of setup.

// Main logic
d3.xml(url, (error, xml) => {
  let xmlDoc = d3.select(xml.documentElement);

  d3.select("body")
    .selectAll("div")
    .data(xmlDoc.selectAll("Main").nodes())
    .enter().append("div")
      .attr("class", "myDiv")
      .style("width", d => d3.select(d).select("Age").text() * 5 + "px")
      .text(d => d3.select(d).select("Name").text());
});
.myDiv{
  background-color: lightblue;
  margin: 4px;
}
<script src="https://d3js/d3.v4.js"></script>

Firstly, we'll change your XML structure, wrapping all your data inside a tag. Traditionally, this tag is named data:

<data>
    <main>
        <name>Rotunda</name>
        <age>43</age>
    </main>
    <main>
        <name>John</name>
        <age>22</age>
    </main>
</data>

After that, inside your d3.xml callback, you could simply use...

xml = xml.getElementsByTagName("main");

However, since inside <main> you have two tags with data, it will be awkward getting that data with textContent. This solution by Mike Bostock is way more interesting:

xml = [].map.call(xml.querySelectorAll("main"), function(d) {
    return {
        name: d.querySelector("name").textContent,
        age: +d.querySelector("age").textContent
    };
});

Now, you just need to use name and age in your code, which will be like this:

d3.xml("thefilehere.xml", function(err, xml) {

    xml = [].map.call(xml.querySelectorAll("main"), function(d) {
        return {
            name: d.querySelector("name").textContent,
            age: +d.querySelector("age").textContent
        };
    });

    d3.select("body")
        .selectAll("div")
        .data(xml)
        .enter()
        .append("div")
        .attr("class", "myDiv")
        .style("width", function(d) {
            return d.age * 5 + "px";
        })
        .text(function(d) {
            return d.name
        });
});

Since I can't use d3.xml in the Stack snippet, here is a plunker with the working code: https://plnkr.co/edit/e9aCsHZnPiV4fBaPvAZv?p=preview

To be able to get the data with d3.xml, you have to be serving thefilehere.xml from a local server.

This will not work if thefilehere.xml is simply a file in the same directory as your HTML & JS.

发布评论

评论列表(0)

  1. 暂无评论