简体   繁体   中英

Cycle through all immediate children of XML DOM by tag name in JavaScript

I have this kind of dynamic XML to parse, where a section tag always has an "allSubSections" tag, which may or may not be filled with more sections (of the exact same xml format). Since each section has the "allSubSections" tag, it could theoretically go very very deep with nested sections, but in real life usage, usually goes only a few levels deep. There is only ever one "allSections" tag in my XML document. Here is the XML:

<poAssessMentVersion>
    <allSections>
        <section>
            <allSubSections>
                <section>
                    <allSubSections></allSubSections>
                </section>
                <section>
                    <allSubSections>
                         <section>
                             <allSubSections></allSubSections>
                         <section>
                    </allSubSections>
                </section>
            </allSubSections>
        <section>
    <allSections>
</poAssessmentVersion>

So I need to cycle through, creating javascript objects out of this xml. As with the XML, an object may have subsections of a similar type to itself. I believe I need to cycle through only the direct children of "allSections" with the tag name of "section" as the first step. How would I do that? I just asked a question Here, about doing that, but not cycling through a collection tag and got a great answer. Now I need the equivilent answer for this collection tag "allSections", Here is my current code:

var theSections = xml.querySelectorAll("poAssessmentVersion>allSections");

    for (i = 0; i < theSections.length; i++) {

        var section = new AssessmentSection();
        section.description = theSections[i].querySelectorAll("AssessmentSection>description")[theSections.length - i].childNodes[0].nodeValue;
        version.allSections.push(section);
    }

Is there some way to cycle through the tag getting only the immeditate children ? I feel like my current code is getting close but The order of my list of sections is not quite right. In my example XML I wrote <AssessmentSection> as <section> but it is really <assessmentSection> in my xml.

I changed your XML:

  • fix unclosed tags
  • replace <section> by <assessmentSection> ( based on your "but it is really <assessmentSection> in my xml" )
  • add <title> node inside each <assessmentSection> as example
<poAssessMentVersion>
  <allSections>

    <!-- level #1 / section 1 -->
    <assessmentSection>
      <title>level #1: section 01</title>

      <allSubSections>

        <!-- level #2 / section 1 -->
        <assessmentSection>
          <title>level #2: section 01</title>
          <allSubSections></allSubSections>
        </assessmentSection>

        <!-- level #2 / section 2 -->
        <assessmentSection>
          <title>level #2: section 02</title>
          <allSubSections>

            <!-- level #3 / section 1 -->
            <assessmentSection>
              <title>level #3: section 01</title>
              <allSubSections></allSubSections>
            </assessmentSection>
            <!-- end of level #3 / section 1 -->

          </allSubSections>
        </assessmentSection>
        <!-- end of level #2 / section 2 -->

      </allSubSections>
    </assessmentSection>
    <!-- end of level #1 / section 1 -->

  </allSections>
</poAssessMentVersion>

JavaScript code

// helper, convert to Array
function toArray(obj) {
  return Array.prototype.slice.call(obj);
}

// return value of the <title> node or empty string
function getSectionTitle(node) {
  try {
    return node.querySelector('title').childNodes[0].nodeValue;
  }
  catch(e) {
    return '';
  }
}

// counter for custom attribute name
var attributeCoutner = 0;

// generate unique attribute name
function getAttributeName() {
  return 'x-custom-attr-' + (attributeCoutner++);
}

// parse "root" <poAssessMentVersion> node
function parseRootNode(node) {
  var attrName = getAttributeName();

  node.setAttribute(attrName, true);
  var selector = '[' + attrName + '] > allSections > assessmentSection';
  var children = node.parentNode.querySelectorAll(selector) || [];
  node.removeAttribute(attrName);

  return toArray(children).map(parseSectionNode);
}

// parse <assessmentSection> node
function parseSectionNode(node) {
  var attrName = getAttributeName();

  node.setAttribute(attrName, true);
  var selector = '[' + attrName + '] > allSubSections > assessmentSection';
  var children = node.parentNode.querySelectorAll(selector) || [];
  node.removeAttribute(attrName);

  return {
    title: getSectionTitle(node),
    subSections: toArray(children).map(parseSectionNode)
  };
}

// start parsing
function parseXml(xml) {
  return parseRootNode(xml.documentElement);
}

// I suppose that you load XML with help AJAX
function onXml(err, xml) {
  if(err) {
    return console.log(err);
  }
  var sections = parseXml(xml);
  console.log(JSON.stringify(sections));
}

The result of console.log will look like this

[
  {
    "title": "level #1: section 01",
    "subSections": [
      {
        "title": "level #2: section 01",
        "subSections": []
      }, 
      {
        "title": "level #2: section 02",
        "subSections": [
          {
            "title": "level #3: section 01",
            "subSections": []
          }
        ]
      }
    ]
  }
]

My answer may not be the best, but this is what I did this before I saw another answer posted here, and it works. I got <allSections> tag by hardcoding the childnode index, then cycled through the list of sections, building my objects from the node index's, like so:

function getTheSections(sectionXML) {
    var section = getSection(sectionXML);
    var theQuestions = sectionXML[0].childNodes;
    for (x = 0; x < theQuestions.length; x++) {
        questionXML = theQuestions[x].childNodes;
        var question = getQuestion(questionXML);
        var theItems = questionXML[0].childNodes;
        for (y = 0; y < theItems.length; y++) {
            itemXML = theItems[y];
            var item = getItem(itemXML);
            question.allItems.push(item);
        }
        section.allQuestions.push(question);
    }
    return section;
}

function getSection(SectionXML) {
    var section = new AssessmentSection();
    section.code = SectionXML[2].childNodes[0];
    section.description = SectionXML[3].childNodes[0];

    section.code = section.code ? section.code.nodeValue : "";
    section.description = section.description ? section.description.nodeValue : "";
    return section;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM