XML to XML with XSLT (Remove, add, alter)

不问归期 提交于 2019-12-12 05:38:07

问题


I have to convert from one XML (XHTML) file to another using XSLT. The rules of transformation are:

  1. <div id="ta12" class="bl" style="dis:bl"> has to be replaced with <div class="pass" value="50">
  2. The values of id="t0b" and "t1b" have to be replaced with id="ta0b8" and "ta3b8" respectively.
  3. <input type="radio" name="o0" id="t0"/> has to be replaced with <input type="radio" name="key0b8" value="0" id="ta0q" class="block" /> (And likewise in the file)

Input file:

    <?xml version="1.0" encoding="utf-8"?>
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    <meta http-equiv="Content-type" content="text/html;  charset=utf-8" />
    </head>  
    <body>
      <div class="iDev">
      <div id="ta12" class="bl" style="dis:bl"></div>

        <div class="q">
          <div id="t0b" class="block">1<span style="color">TEXT1</span>
          </div><br />
          T <input type="radio" name="o0" id="t0"/> 
          F <input type="radio" name="op0" id="f0"/>
          <div id="sfb"></div>
        </div><br />

        <div class="q">
          <div id="t1b" class="block">2<span style="color">TEXT2</span>
          </div><br />
          T <input type="radio" name="o1" id="t1" /> 
          F <input type="radio" name="op1" id="f1" />
          <div id="sfb"></div>
        </div>
      </div>
    </body>
    </html>

Output File:

<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html;  charset=utf-8" />
</head>
<body>
  <div class="iDev">
  <div class="pass" value="50"></div>

    <div class="q">
      <div id="ta0b8" class="block">1<span style="color">TEXT1</span>
      </div><br />
      T<input type="radio" name="key0b8" value="0" id="ta0q" />
      F<input type="radio" name="key0b8" value="1" id="ta1q" />
      <div id="sfb"></div>
    </div><br />

    <div class="q">
      <div id="ta3b8" class="block">2 <span style="color">TEXT2</span>
      </div><br />
      T<input type="radio" name="key3b8" value="0" id="ta0q3" />
      F<input type="radio" name="key3b8" value="1" id="ta1q3" />
      <div id="sfb"></div>
    </div>
  </div>
</body>
</html>

In my XSLT I have created an identity template which includes the entire input file and then I'm trying to do the required modifications. I'm able to do the first task by-

<xsl:template match="xhtml:div[@id='ta12']">
  <xsl:attribute name="class">pa</xsl:attribute>
  <xsl:attribute name="value">10</xsl:attribute>
</xsl:template>

In the output it produces the required Div tag but it removes the <div class="iDev"> tag. Can anyone please tell me the solution for producing the desired output from the given input. Thanking you!


回答1:


I am just going to address your first rule, as this appears to be the focus of your question. If you need help with rules 2 and 3, ask separate questions on them please.

In general, the pattern of solution for XSLT 1.0 to copy an element (non-deeply) will as shown following. By non-deeply, I mean dropping any child nodes.

Listing 1

<xsl:template match="some-element-pattern">
  <xsl:copy>
   <xsl:copy-of select="@*" />
   <xsl:attribute name="my-attrib-to-set">value-to-set</xsl:attribute>
  </xsl:copy>
</xsl:template>

Alternatively, you can replace the xsl:copy-of with xsl:apply-templates if there is some potential for other attribute processing other that straight copying. xsl:apply-templates is the more generic form.

So applying this solution pattern to your case, the template you want is ...

Listing 2

<xsl:template match="xhtml:div[@id='ta12']">
  <xsl:copy>
   <xsl:copy-of select="@*" />
   <xsl:attribute name="class">pa</xsl:attribute>
   <xsl:attribute name="value">10</xsl:attribute>
  </xsl:copy>
</xsl:template>

NB. In a previous solution, I may have given you this inefficient template...

Listing 3

<xsl:template match="xhtml:div[@id='ta12']">
  <xsl:copy>
   <xsl:apply-templates select="@*[not(@class)]" />
   <xsl:attribute name="class">pa</xsl:attribute>
  </xsl:copy>
</xsl:template>

I now realize that this is wrong. The predicate not(@class) is pointless and always returns true, as the attribute:: axis always returns an empty sequence when the focus item is an attribute. The thinking behind listing 3, was to eliminate the extraneous processing of the class attribute in cases where the matched input div element has a class attribute. If you really wanted to gain this kind of efficiency over listing 2, in XSLT 1.0, you could do as listing 4, but the code is some-what ugly, and I am not sure it is worth it.

Listing 4

<xsl:template match="xhtml:div[@id='ta12']">
  <xsl:copy>
   <xsl:apply-templates select="@*[local-name()!='class']
                                  [local-name()!='value']" />
   <xsl:attribute name="class">pa</xsl:attribute>
   <xsl:attribute name="value">10</xsl:attribute>
  </xsl:copy>
</xsl:template>

Listing 4 only works for class and value being in the null namespace. Some alternation is required if the attributes in question are in a namespace.

And here is a final option for you. If you were prepared to sacrifice the generality of xsl:copy and wanted to suck on the juicy marrow of Attribute Value Templates, you could employ listing 5. In listing 5, you can replace the 'pa' and 'va' with Attribute Value Templates, as required. A further disadvantage of this solution, is that it now become necessary, not merely an efficiency measure, to ensure that @class and @value attributes are not processed within the child xsl:apply-templates, or they will over-write the intended literal values.

Listing 5

<xsl:template match="xhtml:div[@id='ta12']">
  <xhtml:div class="pa" value="va">
   <xsl:apply-templates select="@*[local-name()!='class']
                                  [local-name()!='value']" />
  </xhtml:div>
</xsl:template>

And finally, I know you are only interested in XSLT 1.0, but just for a bit of fun, I will mention the general pattern of solution for XSLT 2.0. This is shown in listing 6.

Listing 6

<xsl:template match="some-element-pattern">
  <xsl:copy>
   <xsl:apply-templates select="@* except @my-attrib-to-set" />
   <xsl:attribute name="my-attrib-to-set">value-to-set</xsl:attribute>
  </xsl:copy>
</xsl:template>


来源:https://stackoverflow.com/questions/11733160/xml-to-xml-with-xslt-remove-add-alter

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!