Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rename nodes with XSLT

Tags:

xml

vb.net

xslt

I am trying something very simple, but for some reason it does not work. Basically, I need to rename some nodes in an XML document. Thus, I created an XSLT file to do the transformation.

Here is an example of the XML:

EDIT: Addresses and Address elements occur at many levels. This is what caused me to have to try and apply an XSLT. The Visual Studio typed dataset feature, which creates typed datasets from XSD files does not permit you to have nested references to the same table. Thus, having Businesses/Business/Addresses and Businesses/Business/Contact/Addresses causes the Load() to fail. This is a known issue, and all they tell you is something like "Don't have nested table references...edit your XSD to stop having that." Unfortunately, this means that we have to apply XSLT to make the XML conform to the "hacked" XSD, since the files are coming from a third party vendor.

So, we are very close with the help rendered here. The last couple of things are these:

1.) How can I use the namespace reference in the match attribute of the xsl:template in order to specify that I want to rename Businesses/Business/Addresses to BusinessAddresses, but rename Businesses/Business/Contacts/Contact/Addresses to ContactAddresses?

2.) How can I stop the XSLT from cluttering every new element with explicit namespace references? It is causing extreme bloat in the output.

I created a namespace called "steel", and was having good success with:

<xsl:template match="steel:Addresses>
  <xsl:element name="BusinessAddresses>
</xsl:template>

The obvious problem here is that it renames ALL of the Addresses elements to BusinessAddresses, even though I want some of them named ContactAddresses, and so on. The needless addition of explicit namespace references to all of the renamed nodes is also troublesome.

I tried this sort of thing, but as soon as I add slashes to the match attribute, it is a a syntax error in the XSLT, like so:

<xsl:template match="steel:/Businesses/Business/Addresses">

I feel very close, but need some guidance on how to mix both the namespace usage and a way to use the slashes to select ANY nodes under specific paths.

<?xml version="1.0"?>
<Businesses>
  <Business>
    <BusinessName>Steel Stretching</BusinessName>
    <Addresses>
      <Address>
        <City>Pittsburgh</City>
      </Address>
      <Address>
        <City>Philadelphia</City>
      </Address>
    </Addresses>
    <Contacts>
      <Contact>
        <FirstName>Paul</FirstName>
        <LastName>Jones</LastName>
        <Addresses>
          <Address>
            <City>Pittsburgh</City>
          </Address>
        </Addresses>
      </Contact>
    </Contacts>
  </Business>
  <Business>
    <BusinessName>Iron Works</BusinessName>
    <Addresses>
      <Address>
        <City>Harrisburg</City>
      </Address>
      <Address>
        <City>Lancaster</City>
      </Address>
    </Addresses>
  </Business>
</Businesses>

I need to rename Addresses to BusinessAddresses, and I need to rename Address to BusinessAddress, for every instance of Addresses and Address directly under a Business node. I also need to rename Addresses to ContactAddresses, and I need to rename Address to ContactAddress, for every instance of Addresses and Address directly under a Contact Node.

I have tried several solutions, but none seem to work. They all end up producing the same XML as the original file.

Here is an example of what I have tried:

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

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

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

This has been tried in at least 6 different flavors, complete with stepping through the XSLT debugger in VB.Net. It never executes the template match for Addresses.

Why?

like image 826
Pittsburgh DBA Avatar asked Nov 07 '08 04:11

Pittsburgh DBA


People also ask

Is XSLT deprecated?

The support for XSLT scripting in Data management is deprecated to improve security and data protection within finance and operations apps.

Is XSL same as XSLT?

XSLT is designed to be used as part of XSL. In addition to XSLT, XSL includes an XML vocabulary for specifying formatting. XSL specifies the styling of an XML document by using XSLT to describe how the document is transformed into another XML document that uses the formatting vocabulary.

Is there any benefit of converting XML to XSLT?

XSLT is commonly used to convert XML to HTML, but can also be used to transform XML documents that comply with one XML schema into documents that comply with another schema. XSLT can also be used to convert XML data into unrelated formats, like comma-delimited text or formatting languages such as troff.

Can XSLT transform XML to CSV?

The following XSL Style Sheet (compatible with XSLT 1.0) can be used to transform the XML into CSV. It is quite generic and can easily be configured to handle different xml elements by changing the list of fields defined ar the beginning.


1 Answers

Why might an XSLT fail?

An XSLT will fail because of obvious things like typos. However, the most likely situation relates to namespace usage. If you declared a default namespace for your XML but don't include that in your XSLT, the XSLT won't match the templates as you might expect.

The following example adds the xmlns:business attribute which declares that items qualified by the business prefix belong to the namespace mynamespace.uri. I then used this prefix to qualify the Address and Addresses template matches. Of course, you will need to change the namespace URI to whatever matches the default namespace of your XML file.

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
                xmlns:msxsl="urn:schemas-microsoft-com:xslt"
                xmlns:business="mynamespace.uri"
                exclude-result-prefixes="msxsl">
  <xsl:template match="/">
    <xsl:apply-templates select="@*|node()"/>
  </xsl:template>

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

  <xsl:template match="business:Addresses">
    <xsl:element name="BusinessAddresses">
      <xsl:apply-templates select="@*|node()" />
    </xsl:element>
  </xsl:template>

  <xsl:template match="business:Address">
    <xsl:element name="BusinessAddress">
      <xsl:apply-templates select="@*|node()"/>
    </xsl:element>
  </xsl:template>
</xsl:stylesheet>

How do you match templates based on element location as well as name?

There are several ways to achieve this last part to your problem, BusinessAddress or ContactAddress, but the easiest is to modify the template match attributes to consider parent nodes. If you think of the match attribute as a path into the XML for a node, this option becomes clearer (contents of templates left out for brevity):

<xsl:template match="business:Business/business:Addresses>
</xsl:template>

<xsl:template match="business:Business/business:Addresses/business:Address">
</xsl:template>

<xsl:template match="business:Contact/business:Addresses">
</xsl:template>

<xsl:template match="business:Contact/business:Addresses/business:Address">
</xsl:template>

Other methods exist for achieving this if the match remains based on just the element name, but they're harder to implement, follow, and maintain as they involve the use of conditional checks on the parent node hierarchy of the element being processing, all within the template.

like image 93
7 revs Avatar answered Sep 20 '22 03:09

7 revs