DesignLiquido / xslt-processor

A JavaScript XSLT processor without native library dependencies
GNU Lesser General Public License v3.0
104 stars 32 forks source link

`xsl:template` transformation packs text nodes to the beginning #109

Open jrutila opened 1 month ago

jrutila commented 1 month ago

This test case will show the problem. The output is now XYtest1 even though it should be Xtest1Y. I think it is because the text nodes get siblingPosition of -1 and the xsl:value-of gets bigger than 0. Then, when it is rendering, it sorts the siblings by siblingPosition.

    it('XSLT template with text on both sides', async () => {
        const xmlString = `<root>
          <test name="test1">This text lost</test>
        </root>`;

        const xsltString = `<?xml version="1.0"?>
          <xsl:stylesheet version="1.0">
            <xsl:template match="/">
              <span>X<xsl:value-of select="test/@name" />Y</span>
            </xsl:template>
          </xsl:stylesheet>`;

        const expectedOutString = `<span>Xtest1Y</span>`;

        const xsltClass = new Xslt();
        const xmlParser = new XmlParser();
        const xml = xmlParser.xmlParse(xmlString);
        const xslt = xmlParser.xmlParse(xsltString);

        const outXmlString = await xsltClass.xsltProcess(xml, xslt);

        assert.equal(outXmlString, expectedOutString);
    });
leonelsanchesdasilva commented 1 month ago

@jrutila Thanks again for this new test. I added to our unit tests as well.

This test fails with the following:

    Difference:

    - Expected
    + Received

    - <span>Xtest1Y</span>
    + <span>Ytest1</span>

But the online transformer returns something else:

<span>XY</span>

This happens because the resulting XML doesn't necessarily retain text from the input, if not explicitly mentioned in the template.

Can you please validate if I should implement the behavior from the online transformer? It utilizes XSL implementation in Java.

jrutila commented 1 month ago

Change the value-of line to

span>X<xsl:value-of select="//test/@name" />Y</span>
leonelsanchesdasilva commented 4 weeks ago

@jrutila This problem happens due to this project still not having template precedence implemented. We have a unit test that describes the problem very well. So far, the logic to resolve this works good with one child text node, but not with multiple nodes: https://github.com/DesignLiquido/xslt-processor/blob/main/src/xslt/xslt.ts#L943

Besides the template precedence (which resolves an issue when a more restricted template comes before a least restricted template), I need to think about a logic that writes text nodes differently when they are in a template. One of my initial ideas was to pass a fragment node to write the children of that node, separating commonLogicTextNode into two methods: one that works in a traditional pass, outside of a template (in this case, simply appending nodes to the parent node), and one inside of the template, overwriting text nodes when needed.

As time is being scarce on my end, I would need some time to think about a good solution on this.