1

I'm having trouble getting my XML formatted correctly. I'm pulling data from a mySQL DB and it returns data as such.

<Customers>
<Customer Telephone="#" Country="#" Postcode="#" County="" Town="#" Address2="#" Address1="#" Surname="#" Forename="#" Suffix="#" Middlename="#" Title="#" Id="#"/>
</Customers>

I needed these attributes as element which with a quick search was fairly easy, using XLST the below file.

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl">
<xsl:output method="xml" indent="yes"/>
<xsl:template match="/">
<Company>
<xsl:apply-templates/>
</Company>
</xsl:template>
<xsl:template match="@* | node()">
<xsl:copy>
<xsl:apply-templates select="@* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="@*">
<xsl:element name="{name()}"><xsl:value-of select="."/></xsl:element>
</xsl:template>
<xsl:template match="@*">
<xsl:element name="{name()}"><xsl:value-of select="."/></xsl:element>
</xsl:template>
</xsl:stylesheet>

I end up with data as such.

<Company>
<Customers>
<Customer><Id>#</Id><Title>#</Title><Middlename>#</Middlename><Suffix>#</Suffix><Forename>#</Forename><Surname>#</Surname><Address1>#</Address1><Address2>#</Address2><Town>#</Town><County>#</County><Postcode>#</Postcode><Country>#</Country><Telephone>#</Telephone>
</Customer>
</Customers>
</Company>

However I need to add an '< Addresses > < /Addresses >' Element around (Address1 and Address2) using XSLT but really struggling on how to do this. Pretty much everything I've tried results in an error.

Thanks in advanced,

3 Answers 3

3

Did it occur to you that you have the same template twice in your XSLT stylesheet?

Intervene the identity template at the point where the Customer element is processed. Insert a new element Adresses and inside it, apply templates only to attributes whose name includes "Adress":

<Adresses>
   <xsl:apply-templates select="@*[contains(name(),'Address')]"/>
</Adresses>

Only then apply templates to the remaining content of Customer:

<xsl:apply-templates select="@*[not(contains(name(),'Address'))]|node()"/>

XSLT Stylesheet

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

    <xsl:output method="xml" indent="yes"/>

    <xsl:template match="/">
        <Company>
            <xsl:apply-templates/>
        </Company>
    </xsl:template>

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

    <xsl:template match="Customer">
        <xsl:copy>
            <Adresses>
                <xsl:apply-templates select="@*[contains(name(),'Address')]"/>
            </Adresses>
            <xsl:apply-templates select="@*[not(contains(name(),'Address'))]|node()"/>
        </xsl:copy>
    </xsl:template>

    <xsl:template match="Customer/@*">
        <xsl:element name="{name()}"><xsl:value-of select="."/></xsl:element>
    </xsl:template>

</xsl:stylesheet>

XML Output

<?xml version="1.0" encoding="UTF-8"?>
<Company>
   <Customers>
      <Customer>
         <Adresses>
            <Address2>#</Address2>
            <Address1>#</Address1>
         </Adresses>
         <Telephone>#</Telephone>
         <Country>#</Country>
         <Postcode>#</Postcode>
         <County/>
         <Town>#</Town>
         <Surname>#</Surname>
         <Forename>#</Forename>
         <Suffix>#</Suffix>
         <Middlename>#</Middlename>
         <Title>#</Title>
         <Id>#</Id>
      </Customer>
   </Customers>
</Company>

Please note that the order of attributes is not significant in XML. The XML parser provides the attributes of an element in any order it likes. In your case, this means that the order of child elements of Customer in the output will be arbitrary.


Try this solution online here.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks very much, this works great. Also xsltransform.net is an excellent tool!
1

Use

<xsl:template match="Customer/@Address1">
  <Addresses>
    <Address1>
      <xsl:value-of select="."/>
    </Address1>
    <Address2>
      <xsl:value-of select="../@Address2"/>
    </Address2>
  </Addresses>
</xsl:template>

<xsl:template match="Customer/@Address2"/>

Comments

0

First, your existing stylesheet has conflicting templates: you have no less than 3 templates matching @*.

I would suggest you get a little more specific (and a lot shorter):

XSLT 1.0

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

<xsl:template match="/Customers">
    <Company>
        <xsl:apply-templates/>
    </Company>
</xsl:template>

<xsl:template match="Customer">
    <xsl:copy>
        <xsl:apply-templates select="@*[not(starts-with(name(), 'Address'))]"/>
        <Addresses>
            <xsl:apply-templates select="@*[starts-with(name(), 'Address')]"/>
        </Addresses>
    </xsl:copy>
</xsl:template>

<xsl:template match="@*">
    <xsl:element name="{name()}">
        <xsl:value-of select="."/>
    </xsl:element>
</xsl:template>

</xsl:stylesheet>

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.