Basic XML / XSLT Transformation: Find and Replace

I'm new to XML and XSLT, and spent a little time on what should be a fairly simple case of find and replace. I just can't seem to figure out that the syntax is correct.

The overall goal of this exercise is to replace the "Y" and "N" values ​​in the NewCustomer element with "true" or "false", respectively.

Here's my sample data.

<?xml version="1.0"?>
<CustomerList>
  <Customer>
    <CustomerID>1111</CustomerID>
    <CompanyName>Sean Chai</CompanyName>
    <City>New York</City>
    <NewCustomer>N</NewCustomer>
    </Customer>
  <Customer>
    <CustomerID>1112</CustomerID>
    <CompanyName>Tom Johnston</CompanyName>
    <City>Los Angeles</City>
    <NewCustomer>N</NewCustomer>
  </Customer>
  <Customer>
    <CustomerID>1113</CustomerID>
    <CompanyName>Institute of Art</CompanyName>
    <City>Chicago</City>
    <NewCustomer>Y</NewCustomer>
  </Customer>
</CustomerList>

      

Here's a conversion stylesheet.

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

  <!-- Identity Template (applies to all nodes and will copy all nodes -->
  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:apply-templates select="@* | node()"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="Customer">
    <xsl:choose>
      <xsl:when test="NewCustomer = 'Y'">
        <xsl:text>true</xsl:text>
      </xsl:when>
      <xsl:when test="NewCustomer = 'N'">
        <xsl:text>false</xsl:text>
      </xsl:when>
    </xsl:choose>
  </xsl:template>

</xsl:stylesheet>

      

Here's my conclusion.

  <?xml version="1.0" encoding="utf-8" ?> 
  <CustomerList>false false true</CustomerList> 

      

Here I want it to be output.

<?xml version="1.0"?>
<CustomerList>
  <Customer>
    <CustomerID>1111</CustomerID>
    <CompanyName>Sean Chai</CompanyName>
    <City>New York</City>
    <NewCustomer>false</NewCustomer>
  </Customer>
  <Customer>
    <CustomerID>1112</CustomerID>
    <CompanyName>Tom Johnston</CompanyName>
    <City>Los Angeles</City>
    <NewCustomer>false</NewCustomer>
  </Customer>
  <Customer>
    <CustomerID>1113</CustomerID>
    <CompanyName>Institute of Art</CompanyName>
    <City>Chicago</City>
    <NewCustomer>true</NewCustomer>
  </Customer>
</CustomerList>

      

What am I missing and why? I see that if I omit the items where I examine NewCustomer, all output will be output. However, by choosing to output correctly modified values ​​for NewCustomer results, only their displayed values ​​are displayed. Is there a link to the previous template that I have to do in the second template?

+2


source to share


3 answers


Jim Garrison will disable the attributes of any element NewCustomer

that has them. And Tomalak, as he put it, is a little dirty.

This version is an almost literal translation of your requirement in XSLT:



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

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

  <xsl:template match="NewCustomer/text()[.='Y']">
   <xsl:text>true</xsl:text>
  </xsl:template>

 <xsl:template match="NewCustomer/text()[.='N']">
    <xsl:text>false</xsl:text>
  </xsl:template>

</xsl:stylesheet>

      

The only nodes in the source tree that it does not copy exactly into the result set are text nodes, which are children NewCustomer

and whose value is Y

either N

; for them he instead true

and false

allocates instead of him.

+4


source


If your template matches the client, you intercept all processing for that tag.

Try changing the template just to match NewCustomer by changing the test conditions accordingly (test = ". = 'Y'").

Also note that you will need to create the NewCustomer tag in the output file, because matching it in the custom template is not handled by the identity transformation. You are very close.

Here's the updated template:



<xsl:template match="Customer/NewCustomer">
    <xsl:copy>
        <xsl:choose>
            <xsl:when test=". = 'Y'">
                <xsl:text>true</xsl:text>
            </xsl:when>
            <xsl:when test=". = 'N'">
                <xsl:text>false</xsl:text>
            </xsl:when>
        </xsl:choose>
    </xsl:copy>
</xsl:template>

      

First, it matches NewCustomer as a child of Customer. Then it uses xsl: copy to make a copy of the node (but not the attributes or children). Then it uses your xsl: select to convert the N and Y values ​​to false and true respectively.

The basic concept is that when a pattern matches an input element, the input element is effectively removed from the output and replaced with the content of the matching pattern. In your case, when you matched a customer, the Customer tag and everything inside it was replaced with the content of the template that just generated "true" or "false".

+3


source


The good explanation in Jim Garrison's answer still applies. Here's a concise / alternative approach:

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

  <xsl:template match="@* | node()">
    <xsl:copy>
      <xsl:choose>
        <xsl:when test="self::NewCustomer">
          <xsl:value-of select="boolean(number(translate(., 'YN', '10')))" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:apply-templates select="@* | node()"/>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

      

The expression boolean(number(translate(., 'YN', '10')))

changes 'Y'

to '1'

and 'N'

to '0'

, which is then converted first to a number and then to a boolean. Boolean will be represented as 'true'

or 'false'

respectively.

This is a bit messy as it doesn't handle values ​​other than 'Y'

or 'N'

, but it will output 'false'

for any value other than 'Y'

.

It's just a splash space. If you want, you can replace it with <xsl:choose>

, for example, using Jim Garrison.

0


source







All Articles