views:

49

answers:

5

Hi, I learned about XSLT and XPath at w3schools,but it's not exactly what I wanted.. There are only examples about transforming XSLT 2 HTML, this is pretty easy,but I need a XML 2 XML transformation and can't find a good tutorial with examples...I downloaded MSXSL.exe but can't find exempls about using it to transform XML... Can anyone write a sample? I have customers.xml like :

<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<!--Customers table from Northwind database-->
<Customers>
  <Customer CustomerID="ALFKI">
    <CompanyName>Alfreds Futterkiste</CompanyName>
    <Country>Germany</Country>
    <Orders>
      <Order OrderID="10643">
        <ShipCity>Berlin</ShipCity>
      </Order>
      <Order OrderID="10692">
        <ShipCity>Berlin</ShipCity>
      </Order>
      <Order OrderID="10702">
        <ShipCity>Berlin</ShipCity>
      </Order>
      <Order OrderID="10835">
        <ShipCity>Berlin</ShipCity>
      </Order>
      <Order OrderID="10952">
        <ShipCity>Berlin</ShipCity>
      </Order>
      <Order OrderID="11011">
        <ShipCity>Berlin</ShipCity>
      </Order>
    </Orders>
  </Customer>
  <Customer CustomerID="ANATR">
    <CompanyName>Ana Trujillo Emparedados y helados</CompanyName>
    <Country>Mexico</Country>
    <Orders>
      <Order OrderID="10308">
        <ShipCity>México D.F.</ShipCity>
      </Order>
      <Order OrderID="10625">
        <ShipCity>México D.F.</ShipCity>
      </Order>
      <Order OrderID="10759">
        <ShipCity>México D.F.</ShipCity>
      </Order>
      <Order OrderID="10926">
        <ShipCity>México D.F.</ShipCity>
      </Order>
    </Orders>
  </Customer>
</Customers>

And trying to generate another XML that contains only "country" nodes.. How to write it?

+3  A: 

It's exactly the same, you just need to use the method attribute of the output element accordingly.

Paul Butcher
+1, examples here: http://www.w3schools.com/xsl/el_output.asp
Bruno
+3  A: 

Something about like this should work...

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
  <xsl:output method="xml" indent="yes"/>

  <xsl:template match="/">
    <Countries>
      <xsl:apply-templates select="//Country" mode="copyNode" />
    </Countries>
  </xsl:template>

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

... Here is the result ...

<?xml version="1.0" encoding="utf-8"?>
<Countries>
  <Country>Germany</Country>
  <Country>Mexico</Country>
</Countries>
Matthew Whited
Guess I don't need the Microsoft namespace if I'm not using it.
Matthew Whited
I should probably add this will include any child nodes of the `<Country />` elements if they existed.
Matthew Whited
+1  A: 

Its pretty simple, you just need to write xml instead of html inside your xslt templates.

The following (untested) should get you started:

<xsl:template match="/">
  <Countries>
    <xsl:apply-templates select="Customers/Customer" />
  </Countries>
</xsl:template>

<xsl:template match="Customer">
  <Country>
    <xsl:value-of select="Country" />
  </Country>
</xsl:template>

Also, for a XSLT processor I heartily recommend Visual Studio - it has an excellent XSLT debugger which will help you out no end in understanding your stylesheets. (I'm not sure if this functionality is included in the express editions...)

Kragen
A: 

In pull style, this should be the shorter stylesheet expressing this transformation:

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:template match="text()"/>
    <xsl:template match="@*|/*|Country|Country/text()">
        <xsl:copy>
            <xsl:apply-templates select="@* | node()"/>
        </xsl:copy>
    </xsl:template>
</xsl:stylesheet>

Output:

<Customers>
    <Country>Germany</Country>
    <Country>Mexico</Country>
</Customers>
Alejandro
A: 

Here are two different solutions: a simple one and a more complicated one that produces only the unique countries:

.1. Get all Country elements:

<xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

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

 <xsl:template match="*[not(self::Country)]">
  <xsl:apply-templates select="*"/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on the provided XML document, the wanted, correct result is produced:

<Country>Germany</Country>
<Country>Mexico</Country>

.2. Find all unique countries:

<xsl:stylesheet version="2.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform"&gt;
    <xsl:output omit-xml-declaration="yes" indent="yes"/>

    <xsl:key name="kCountryByVal" match="Country" use="."/>

 <xsl:template match="/">
     <xsl:copy-of select=
       "/*/*/Country[generate-id()
                    =
                     generate-id(key('kCountryByVal',.)[1])
                    ]
       "/>
 </xsl:template>
</xsl:stylesheet>

when this transformation is applied on any XML document with the structure of the provided document where some Country elements may have the same value, only one Country element for each different value is produced.

With the provided XML document we still get the same result:

<Country>Germany</Country>
<Country>Mexico</Country>
Dimitre Novatchev