tags:

views:

670

answers:

4

I have one set of documents that implicitly define the allowed fields for a second set of objects that have to be transformed into a third set of documents ( which "rules" document to use depends upon the content of the file being transformed) e.g.

<!-- One example rules document --> 
<document object="obj1_rules">
<field name="A"/>
<field name="B"/>
<field name="C"/>
</document>

<!-- Document to be tranformed based upon obj1_rules--> 
<document object="obj1">
<field name="A"/>
<field name="B"/>
<field name="C"/>
<field name="D"/>
<field name="E"/>
</document>

<!-- Desired result--> 
<document object="obj1">
<newfield name="A"/>
<newfield name="B"/>
<newfield name="C"/>
</document>

Is it possible to do this transformation using xslt?

I see that "There is no way in XSLT of constructing XPath expressions (e.g. variable references) at run-time." So I am out of luck, or I am just looking at this problem incorrectly? Thanks!

+1  A: 

Maybe I'm oversimplifying, but is there a reason why your "rules document" cannot simply be an XSLT?

Jim H.
+1. That would be my preferred way of doing it.
Mitch Wheat
Sadly, because these are the requirements I was handed. :-)
Atlas1j
+1  A: 

Well, I can see why you'd like to have rules in a simple xml file rather than in a full-fledged xsl stylesheet but you're simply skipping a step.

You need to make a xsl stylesheet that will transform your xml rule document into a xsl stylesheet that you will then apply onto your source xml.

The trick is with namespaces and not getting confused by the mix of xsl rules applied and xsl rules generated.

    <?xml version="1.0" ?>
    <xsl:stylesheet
        xmlns="YOUR_NAMESPACE_HERE"
        xmlns:output="http://www.w3.org/1999/XSL/Transform&quot;
        xmlns:xsl="http://www.w3.org/1999/XSL/Transform&quot;
        version="2.0">

        <xsl:output
            method="xml"
            indent="yes"
            media-type="text/xsl" />

        <xsl:template match="/">
            <output:stylesheet version="2.0">
                <xsl:apply-templates />
            </output:stylesheet>
        </xsl:template>

        <xsl:template match="document[@object]">
            <output:template match="document[@object='{@object}']">
                <output:copy>
                    <xsl:apply-templates />
                </output:copy>
            </output:template>
        </xsl:template>

        <xsl:template match="field[@name]">
            <output:if test="field[@name='{@name}']">
                <output:copy-of select="field[@name='{@name}']" />
            </output:if>
        </xsl:template>

    </xsl:stylesheet>

I presumed you'd use the same document object attribute in the rules and in the documents themselves (this is much simpler imo).

So, you run your rules document through the stylesheet above. The result is a new xsl stylesheet that does exactly what you describe in your xml rule document. You then apply this resulting stylesheet onto your source document and you should get the result you expect.

Julian Aubourg
+2  A: 

Here is a simple solution:

This transformation:

<xsl:stylesheet version="1.0" 
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>
    <xsl:strip-space elements="*"/>
<!--                                                   -->  
    <xsl:variable name="vrtfRules">
     <document object="obj1_rules">
      <field name="A"/>
      <field name="B"/>
      <field name="C"/>
     </document>
    </xsl:variable>
<!--                                                   -->  
    <xsl:variable name="vRules" select=
     "document('')/*/xsl:variable
                   [@name = 'vrtfRules']
                     /*
     "/>
<!--                                                   -->  
    <xsl:template match="node()|@*">
     <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
     </xsl:copy>
    </xsl:template>
<!--                                                   -->  
    <xsl:template match="field">
      <xsl:if test="@name = $vRules/*/@name">
        <newfield>
          <xsl:apply-templates select="node()|@*"/>
        </newfield>
      </xsl:if>
    </xsl:template>
</xsl:stylesheet>

when applied on the originaly-provided source XML document:

<document object="obj1">
    <field name="A"/>
    <field name="B"/>
    <field name="C"/>
    <field name="D"/>
    <field name="E"/>
</document>

produces the desired result:

<document object="obj1">
   <newfield name="A"/>
   <newfield name="B"/>
   <newfield name="C"/>
</document>

Note that the "rules document" is within the stylesheet just for compactness. When it is a separate document, just the document() function used will need to be adjusted with the actual href.

Dimitre Novatchev