简体   繁体   中英

Convert xml using xslt

I have the following xml:

<fo:block font-weight="bold" font-style="italic">Some Text Here</fo:block>

I need to convert it to the following using xsl:

{\b\iSome Text Here\i0\b0\par}

So far i managed to select the block element using:

<xsl:template match="fo:block">
<xsl:text>{</xsl:text>
    <xsl:apply-templates />
<xsl:text>\par}</xsl:text></xsl:template>

and i am outputting: {Some Text Here\\par}

I am struggling with the attributes and inserting it using xsl, can anyone give me an example of selecting those attributes and getting the desired result?

There's a fairly simple way of doing this generically, using template modes, and the <xsl:sort> instruction.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

<xsl:template match="fo:block">
  <xsl:text>{</xsl:text>
  <xsl:apply-templates select="@*" mode="prefix" />
  <xsl:apply-templates select="node()" />
  <xsl:apply-templates select="@*" mode="suffix">
    <xsl:sort order="descending"/>
  </xsl:apply-templates>
  <xsl:text>\par}</xsl:text>
</xsl:template>

<xsl:template match="@font-weight['bold']" mode="prefix">\b</xsl:template>
<xsl:template match="@font-style['italic']" mode="prefix">\i</xsl:template>

<xsl:template match="@font-weight['bold']" mode="suffix">\b0</xsl:template>
<xsl:template match="@font-style['italic']" mode="suffix">\i0</xsl:template>

</xsl:stylesheet>

The <xsl:sort order="descending" /> processes the attributes in reverse order the second time, when the 'suffix' mode is used.

Strictly speaking the select="node()" in the middle of the main template is superfluous, but it makes it clearer when reading that only nodes are processed, and not attributes.

You could make it a little easier to add new attributes by replacing the existing suffix mode templates with this:

<xsl:template match="@*" mode="suffix">
  <xsl:apply-templates select="." mode="prefix" />
  <xsl:text>0</xsl:text>
</xsl:template>

This just uses the prefix's template, and adds the extra 0 on the end. You can always override it if some attributes can't be handled as generically as this.

<xsl:template match="fo:block">
    <xsl:text>{</xsl:text>
        <xsl:if test="@font-weight = 'bold'">\b</xsl:if>
        <xsl:if test="@font-style = 'italic'">\i</xsl:if>

        <xsl:apply-templates />

        <xsl:if test="@font-style = 'italic'">\i0</xsl:if>
        <xsl:if test="@font-weight = 'bold'">\b0</xsl:if>
    <xsl:text>\par}</xsl:text>
</xsl:template/>

Also check w3schools.com for further readings on XSLT.

This transformation is more generic and extesnsible :

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my">
 <xsl:output method="text"/>

 <xsl:template match="/*">
  <xsl:variable name="vAttributesResult">
   <xsl:call-template name="processAttributes"/>
  </xsl:variable>

  <xsl:value-of select=
   "concat(substring-before($vAttributesResult, ' '),
           .,
           substring-after($vAttributesResult, ' ')
           )
   "/>
 </xsl:template>

 <xsl:template name="processAttributes">
  <xsl:param name="pattrList" select="@*"/>
  <xsl:param name="pResult" select="' '"/>

  <xsl:choose>
      <xsl:when test="not($pattrList)">
       <xsl:value-of select="$pResult"/>
      </xsl:when>
      <xsl:otherwise>
       <xsl:variable name="vthisResult">
        <xsl:apply-templates select="$pattrList[1]">
         <xsl:with-param name="pResult" select="$pResult"/>
        </xsl:apply-templates>
       </xsl:variable>

       <xsl:call-template name="processAttributes">
        <xsl:with-param name="pattrList" select="$pattrList[position()>1]"/>
        <xsl:with-param name="pResult" select="$vthisResult"/>
       </xsl:call-template>
      </xsl:otherwise>
  </xsl:choose>
 </xsl:template>

 <xsl:template match="@font-weight[.='bold']">
  <xsl:param name="pResult"/>
  <xsl:value-of select="concat('\b', $pResult, '\b0')"/>
 </xsl:template>

 <xsl:template match="@font-style[.='italic']">
  <xsl:param name="pResult"/>
  <xsl:value-of select="concat('\i', $pResult, '\i0')"/>
 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document (corrected to be well-formed):

<fo:block font-weight="bold" font-style="italic"
xmlns:fo="some:fo">Some Text Here</fo:block>

the wanted result is produced :

\i\bSome Text Here\b0\i0

Do note :

  1. You can easily add processing for as many new combinations of attributes/values as necessary -- just add a new template for a new attribute.

  2. If order is important, use these templates to process attributes:

--

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output method="text"/>

    <xsl:template match="/*">
        <xsl:text>{</xsl:text>
        <xsl:apply-templates select="@*">
         <xsl:with-param name="pSuff" select="''"/>
         <xsl:sort select="position()"/>
        </xsl:apply-templates>

        <xsl:apply-templates select="node()" />

        <xsl:apply-templates select="@*">
         <xsl:with-param name="pSuff" select="'0'"/>
         <xsl:sort select="position()" order="descending"/>
        </xsl:apply-templates>

        <xsl:text>\par}</xsl:text>
    </xsl:template>

    <xsl:template match="@font-weight['bold']">
      <xsl:param name="pSuff"/>
      <xsl:value-of select="concat('\b',$pSuff)"/>
    </xsl:template>

    <xsl:template match="@font-style['italic']">
      <xsl:param name="pSuff"/>
      <xsl:value-of select="concat('\i',$pSuff)"/>
    </xsl:template>
</xsl:stylesheet>

This transformation borrows the idea fron the answer by @Flynn1169 and significantly simplifies it (only 3 templates instead of t, and no modes) and, most significantly, presents the results according to the lexical order of the attributes.

In this case the result matches the lexical order of the attributes, not their sorted names ! :

If we have this XML document :

    <fo:block font-style="italic" font-weight="bold" 
xmlns:fo="some:fo">Some Text Here</fo:block>

the result now is:

{\i\bSome Text Here\b0\i0\par}

Remark : While there is nothing as "attribute order" in the XPath data model, all the XSLT processors I am working with (more than 9) in push-style mode produce the results of processing attributes according to their lexical order.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM