goutham_kgh
goutham_kgh

Reputation: 167

XML to XSLT pattern doesn't match

I am playing around with XSLT for a small task. I have the following XML:

 <PFeed>
  <PID> MyProcess </PID>
  <Version>1</Version>
  <MetaData>
      <Id> MyMetadataId </Id>
  </MetaData>
 <AllFeeds>
   <FeedContent>
      <Id> FeedContentId </Id>
   </FeedContent>
 </AllFeeds>
</PFeed>

I want to extract FeedContentId as text from this XML.

This is the XSLT code I have:

<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> 
  <xsl:output method="text" encoding="UTF-8" />
  <xsl:template match="/PFeed/AllFeeds/FeedContent">
        <xsl:value-of select="Id"/>
  </xsl:template>
</xsl:stylesheet>

But instead of outputting FeedContentId I get the following: MyProcess 1 MyMetadataId FeedContentId

Can you please point out what I am missing ?

Upvotes: 3

Views: 47

Answers (2)

Mathias M&#252;ller
Mathias M&#252;ller

Reputation: 22647

The output of your current XSLT code is:

MyProcess 1 MyMetadataId FeedContentId

which is all the text inside the input XML document. This is because there is only one template in your stylesheet, and it does not apply to everything in the input document. For a part of the input, the XSLT processor has to resort to predefined default templates.

The default template for elements skips elements, the default template for text nodes copies those text nodes, and that's exactly what happens.

A solutions is to directly target the content you are interested in (/PFeed/AllFeeds/FeedContent/Id) with one template and write another template that matches text nodes:

<xsl:template match="text()"/>

to override the default behaviour for text nodes.

Also, if your output format is plain text, you should use

<xsl:output method="text"/>

and possibly get rid of extra spaces:

<xsl:strip-space elements="*"/>

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="text"/>
  <xsl:strip-space elements="*"/>

  <xsl:template match="/PFeed/AllFeeds/FeedContent/Id">
     <xsl:value-of select="."/>
  </xsl:template>

  <xsl:template match="text()"/>

</xsl:stylesheet>

Text Output

 FeedContentId 

Try this solution online here.

Upvotes: 1

Maximilian Gerhardt
Maximilian Gerhardt

Reputation: 5353

You first need a template to match the root element, then match what's inside. This XSLT now works again. (Test e.g. on http://www.w3schools.com/xsl/tryxslt.asp?xmlfile=cdcatalog&xsltfile=cdcatalog)

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

  <xsl:template match="/PFeed">
    <xsl:apply-templates select="AllFeeds/FeedContent"/>
  </xsl:template>

  <xsl:template match="FeedContent">
      <xsl:value-of select="Id"/>
  </xsl:template>
</xsl:stylesheet>

Output after transformation:

FeedContentId

However, the same thing (getting the /PFeed/AllFeeds/FeedContent/Id node) could also be achieved by evaluating the above simple XPath expression.

Upvotes: 2

Related Questions