Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
170 views
in Technique[技术] by (71.8m points)

xml - xslt - replace content by a reference when this content is shared by 2 files

Given several XML files that I will call source1.xml that look like this for example (nodes may vary):

Source files

<root id="s1">
    <title>example</title>
        <p>Some body text.</p>
        <div>
            <ul>
                <li>Some list item</li>
                <li>Some list item</li>
            </ul>
            <p>qqwerty</p>
            <p>asdfg</p>
        </div>
</root>

Reference files

And other files that I'll call reference1.xml that look like this:

<root>    
    <div>
        <ul>
            <li>Some list item</li>
            <li>Some list item</li>
        </ul>
        <p>qqwerty</p>
        <p>asdfg</p>
     </div>
<root>

And reference2.xml that looks like this:

<root>    
    <div>
       <p>Some body text.<p>
    </div>
</root>    

There can only be one div element in each of these files.

Mapping file

To map these together I have a file that looks like this:

<references>  
   <input src="source1.xml" id="s1">
      <reference>T:	emp
eference1.xml</reference>
      <reference>T:	emp
eference2.xml</reference>
   </input>
</references>

Expected result

I would like to get the following:

<root id="s1">
    <title>example</title>
        <link href="reference2.xml"/>
        <div>
            <link href="reference1.xml"/>
        </div>
</root>

So the idea is that I want to see the whole content of the div node of reference.xml can be found AS/IS in source.xml and if so, just insert a reference to it in the source. It must a "dumb replace", if I find it, I replace it in the source, wherever the content might be, no matter of the parent element or whatever.

If only certain elements are found in the source, this is a no go, no link is created. It must be the exact same thing for a link to be created.

How do I do this with XSLT 2.0?

Right now, with the code suggested in the answer I get the following output:

<root id="s1">
    <title>example</title>
        <link href="reference2.xml"/>
        <div>
            <link href="reference1.xml"/>
            <link href="reference1.xml"/>
            <link href="reference1.xml"/>
        </div>
</root>

Instead of :

<root id="s1">
    <title>example</title>
        <link href="reference2.xml"/>
        <div>
            <link href="reference1.xml"/>
        </div>
</root>
See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

If it is only a single element to be matched against then doing

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:param name="ref-doc-uri" select="'reference.xml'"/>
    <xsl:variable name="ref-doc" select="doc($ref-doc-uri)"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* , node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*[deep-equal(., $ref-doc//ComponentDefinition/*)]">
        <link href="{$ref-doc-uri}"/>
    </xsl:template>

</xsl:stylesheet>

should suffice.

Based on your edit of the requirements I have enhanced the stylesheet as

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
    xmlns:xs="http://www.w3.org/2001/XMLSchema"
    exclude-result-prefixes="xs"
    version="2.0">

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:variable name="main-doc-uri" select="document-uri()"/>

    <xsl:param name="ref-list-doc-uri" select="'reference-list.xml'"/>
    <xsl:variable name="ref-list-doc" select="doc($ref-list-doc-uri)"/>

    <xsl:variable name="ref-docs" select="document($ref-list-doc/references/input[resolve-uri(@src) = $main-doc-uri]/reference)"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* , node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <xsl:variable name="matched-doc" select="$ref-docs[ReusableComponent/ComponentDefinition/*[deep-equal(., current())]]"/>
        <xsl:choose>
            <xsl:when test="$matched-doc">
                <link href="{tokenize(document-uri($matched-doc), '/')[last()]}"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:next-match/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

where I assume that reference-list.xml has a structure like

<references>
    <input src="source1.xml">
        <reference>ref1.xml</reference>
        <reference>ref2.xml</reference>
    </input>
</references>

I haven't tried that code with several source and reference files, however, so you need to test yourself.

If there can be different namespaces used in the reference document then perhaps using a wild card helps:

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

    <xsl:strip-space elements="*"/>
    <xsl:output indent="yes"/>

    <xsl:variable name="main-doc-uri" select="document-uri()"/>

    <xsl:param name="ref-list-doc-uri" select="'reference-list.xml'"/>
    <xsl:variable name="ref-list-doc" select="doc($ref-list-doc-uri)"/>

    <xsl:variable name="ref-docs" select="document($ref-list-doc/references/input[resolve-uri(@src) = $main-doc-uri]/reference)"/>

    <xsl:template match="@* | node()">
        <xsl:copy>
            <xsl:apply-templates select="@* , node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="*">
        <xsl:variable name="matched-doc" select="$ref-docs[*:ReusableComponent/*:ComponentDefinition/*[deep-equal(., current())]]"/>
        <xsl:choose>
            <xsl:when test="$matched-doc">
                <link href="{tokenize(document-uri($matched-doc), '/')[last()]}"/>
            </xsl:when>
            <xsl:otherwise>
                <xsl:next-match/>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>

</xsl:stylesheet>

That way, for the input

<?xml version="1.0" encoding="UTF-8"?>
<root id="s1">
    <title>example</title>
    <p>Some body text.</p>
    <div>
        <ul>
            <li>Some list item</li>
            <li>Some list item</li>
        </ul>
    </div>
</root>

I get the output

<?xml version="1.0" encoding="UTF-8"?>
<root id="s1">
   <title>example</title>
   <link href="reference201605270302.xml"/>
   <div>
      <link href="reference201605270301.xml"/>
   </div>
</root>

The documents to be loaded are selected based on a comparison of the URIs in resolve-uri(@src) = $main-doc-uri, if you want to do that based on the id attribute then I guess you want <xsl:variable name="ref-docs" select="document($ref-list-doc/references/input[@id = $main-doc/*/@id]/reference)"/> with a global variable <xsl:variable name="main-doc" select="/"/>.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...