Page 1 of 1

How to exclude namespace (re)declarations in Schematron Quick fixes

Posted: Wed Jul 09, 2025 1:46 pm
by fred_a
I'm working with XML documents that have the relevant namespaces declared solely on the root element, and (for mostly stylistic reasons) I'd like to retain that.

When a schematron quick fix containing an xsl template operates on a fragment within the document, the XSLT processor will insert a (redundant) xmlns="..." declaration onto descendants of the element being replaced, thus ignoring the declarations already present in the broader, original document context.

Are there any methods that can be used to avoid this behaviour?

Here's a simplified example to reproduce the behaviour:

Input XML:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns:xlink="http://www.w3.org/1999/xlink">
<fig>
<caption>
<p>This is the title <ext-link ext-link-type="uri" xlink:href="https://schematron.com/">link</ext-link>.</p>
<p>This is the caption.</p>
</caption>
</fig>
</article>
Schematron:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<schema
   xmlns="http://purl.oclc.org/dsdl/schematron"
   xmlns:xlink="http://www.w3.org/1999/xlink"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:sqf="http://www.schematron-quickfix.com/validator/process"
   queryBinding="xslt3">
    
    <xsl:template match="." mode="customCopy">
        <xsl:copy copy-namespaces="no">
            <xsl:apply-templates select="*|@*|text()|comment()|processing-instruction()" mode="customCopy"/>
        </xsl:copy>
    </xsl:template>
    
    <sqf:fixes>
        <sqf:fix id="replace-p-to-title">
            <sqf:description>
                <sqf:title>Change the p to title</sqf:title>
            </sqf:description>
            <sqf:replace match="./p[1]">
                <xsl:element name="title">
                    <xsl:apply-templates select="node()|comment()|processing-instruction()" mode="customCopy"/>
                </xsl:element>
            </sqf:replace>
        </sqf:fix>
    </sqf:fixes>
    
    <pattern id="fig">
        <rule context="fig/caption" id="fig-caption-checks">
            <report test="not(title) and (count(p) gt 1)" 
                role="warning" 
                sqf:fix="replace-p-to-title"
                id="fig-caption-1">Caption for fig doesn't have a title, but there are mutliple paragraphs. Is the first paragraph actually the title?</report>
        </rule>
    </pattern>
    
</schema>
Output after fix:

Code: Select all

<?xml version="1.0" encoding="UTF-8"?>
<article xmlns:xlink="http://www.w3.org/1999/xlink">
<fig>
<caption>
<title>This is the title <ext-link ext-link-type="uri" xlink:href="https://schematron.com/" xmlns:xlink="http://www.w3.org/1999/xlink">link</ext-link>.</title>
<p>This is the caption.</p>
</caption>
</fig>
</article>
Note the xmlns:xlink="http://www.w3.org/1999/xlink" declaration on the new title element.

I'm working with oXygen XML Editor version 26.

Re: How to exclude namespace (re)declarations in Schematron Quick fixes

Posted: Wed Jul 09, 2025 5:29 pm
by tavy
This is not an Oxygen issue; it's more of an XSLT issue. The copy template will always add the namespace declaration to the result element. I don't think there's any way to avoid this. If you create an XSLT with a copy template, you will get the same result.

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xlink="http://www.w3.org/1999/xlink" exclude-result-prefixes="xlink" version="3.0">

<xsl:template match="p">
<xsl:element name="title">
<xsl:apply-templates mode="customCopy" select="node()"/>
</xsl:element>
</xsl:template>

<xsl:template match="node() | @*" mode="customCopy">
<xsl:copy copy-namespaces="false">
<xsl:apply-templates select="node() | @*" mode="customCopy"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
In Oxygen, you can avoid adding this namespace declaration if you use the quick fix option in the Author page. In this case, post-processing is performed to remove the namespace declaration when the namespace is already declared on the root element.

Re: How to exclude namespace (re)declarations in Schematron Quick fixes

Posted: Wed Jul 09, 2025 6:41 pm
by fred_a
That makes sense, and good to know about the Author mode trick.

Sounds like I have a couple of options (use author mode and/or do my own post-processing), or I can just stop being so picky.

Thanks very much for your time looking into this.

Re: How to exclude namespace (re)declarations in Schematron Quick fixes

Posted: Thu Jul 10, 2025 4:18 pm
by george
Hi Fred,

I discussed with Octavian to see what can be done and it turns out we do perform a namespace normalization in the sense of removing unnecessary namespace declarations, but only on the root element that is inserted, not deep on any element. Thus, if you add the in-scope namespaces on the element that you want to insert, that will solve your problem 🙂. The namespaces will not be declared again inside the fragment, because they are already declared on the root element of the fragment, and the ones from the root element will be normalized on insertion.

So, instead of

Code: Select all

           <sqf:replace match="./p[1]" >
                <xsl:element name="title">
                    <xsl:apply-templates select="node() | comment() | processing-instruction()"
                        mode="customCopy"/>
                </xsl:element>
            </sqf:replace>

--- use -->

Code: Select all

           <sqf:replace match="./p[1]" >
                <xsl:element name="title">
                    <xsl:copy-of select="namespace-node()"/>
                    <xsl:apply-templates select="node() | comment() | processing-instruction()"
                        mode="customCopy"/>
                </xsl:element>
            </sqf:replace>

Best Regards,
George

Re: How to exclude namespace (re)declarations in Schematron Quick fixes

Posted: Thu Jul 10, 2025 5:10 pm
by fred_a
Hi George,

Many thanks for this - very helpful, it solves my use-case. After scratching my head for a while, not understanding why it worked in the sample file but not in my real implementation, I realised that the inclusion of schematron namespace declaration interfered:

Code: Select all

<schema xmlns="http://purl.oclc.org/dsdl/schematron">
...
<ns uri="http://www.w3.org/1999/xlink" prefix="xlink"/>
...
Removing this from the schema (and using a wildcard prefix where required) solved the issue. Just mentioning in case this is helpful for anyone else.

Thanks once again for your time in helping me with this.

All the best,
Fred