Suamere
Suamere

Reputation: 6258

E4X (Rhapsody) - Iterate generic children, lowest descendant fails

Assuming I have this XML:

<Level1>
    <Level2>
        <Level3>
            <Level4>
                Level 4 Inner Text
            </Level4>
        </Level3>
    </Level2>
</Level1>

But of course, imagine more nodes than a single path of depth...

Using E4X, which I am forced to use through an application called Rhapsody, I can essentially write a recursive function like so:

function iterateXML(xml) {
    log.info(xml.name());  // like console.log
    for each (var child in xml.*) {  // .* == get all children
        iterateXML(child);
    }
}

I would (essentially) expect something like this:

Level1
Level2
Level3
Level4
// EXCEPTION... Cuz Level 4 Inner Text was passed in, which doesn't have a .name()

Instead, what I get is this:

Level1
Level2
Level3
// EXCEPTION... Cuz Level 4 Inner Text was passed in, which doesn't have a .name()

What seems to be happening is that, any time a child xml from xml.* is found to have no descendants, just text, then it completely forgets that node is an XML node, and just returns me the inner text. So I never get to actually SEE the Level4 child XML node, I just get back the inner text.

The same thing happens if the lowest node is Level 2, or level 100. It essentially skips the last one. If the current target is <lowestNode>SomeValue</lowestNode>, it only gives me SomeValue.

Similarly, this XML:

<Root>
    <Child>
        <GrandChild>Value1</GrandChild>
    </Child>
    <Child>
        Value2
    </Child>
</Root>

with this code:

function iterateXML(xml) {
    for each (var child in xml.*) {  // .* == get all children
        log.info(child.toString());
    }
}

would evaluute to:

<Child><GrandChild>Value1</GrandChild></Child>
Value2

Because the first node has an XML descendant, so it treats it as full XML. But the second one does not have an XML descendant, so it just strips off the XML and gives me the value.

Very inconsistent.

Upvotes: 0

Views: 124

Answers (2)

agermano
agermano

Reputation: 1729

For what it's worth, when I run your first example in the Mozilla Rhino engine, I get the following, so I think you might have been encountering a bug in the Rhapsody implementation.

Level1
Level2
Level3
Level4
null // no exception. Calling .name() on a text node returns null

What you encountered with .toString() is the documented behavior of that method. When calling .toString() on an xml node of type element, if the element has simple content then the simple content is returned. Otherwise, the same output as calling the .toXMLString() method is returned.

Upvotes: 0

Suamere
Suamere

Reputation: 6258

I believe there are a lot of intricacies of the old E4X and it is simply difficult to use. But the solution I have found is in a method I stumbled upon. It requires a bit of extra checking and manual code, but no issue:

if (xml.hasSimpleContent()) {
    var attributes = xml.@*; //or xml.attributes() if that works
    var name = xml.name();
    var value = xml.toString(); // unfortunately evaluates to that simpleContent, but I can use that.
}

Seems simple now that I see it, but the inconsistency of toString() threw things off

Upvotes: 0

Related Questions