Jump to content

How to combine data?


Trekstuff

Recommended Posts

Hello,Sorry if this question has been asked before, this is the first time I encounter this problem. I have 2 XML files1st file:<record ID = "1"> <title>some text</title></record><record ID = "2"> <title>more text</title></record><record ID = "3"> <title>another text</title></record>2nd file:<info ID = "1"> <desc>some information here</desc> <url>http://some_links_ here</url></info><info ID = "2"> <desc>more information here</desc> <url>http://more_links_ here</url></info><info ID = "3"> <desc>another information here</desc> <url>http://another_linsk_here</url></info>I need to insert information from the second file into first by matching ID records. Leaving structure and information in original file intact, just adding matching records from the second file, so the output result will be:<record ID = "1"> <title>some text</title> <desc>some information here</desc> <url>http://some_links_ here</url></record><record ID = "2"> <title>more text</title> <desc>more information here</desc> <url>http://more_links_ here</url></record><record ID = "3"> <title>another text</title> <desc>another information here</desc> <url>http://another_linsk_here</url></record>Not sure what approach to use - any help is appreciated.Thanks in advance,Yuriy.

Link to comment
Share on other sites

In any case, you'll need to use the document() function. It's the only XSLT based way to process a second XML from an initial one.For your case, I think the following would do:

<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">	<xsl:template match="/*">		<records>			<xsl:apply-templates/>		</records>	</xsl:template>	<xsl:template match="record">		<xsl:variable name="ID" select="@ID"/>		<xsl:copy>			<xsl:copy-of select="document('File.xml')/*/record[@ID = $ID]/*"/>			<xsl:apply-templates/>		</xsl:copy>	</xsl:template>	<xsl:template match="*|@*">		<xsl:copy>			<xsl:apply-templates/>		</xsl:copy>	</xsl:template></xsl:stylesheet>

Match this file on one of the files and replace the value of the document() funciton (line 11) with the actual location of the other file. The initial template (/*) was created in order to provide a new element for the new XML, but since I don't know what root element are using your other XMLs, I suppose you may want to adjust that.

Link to comment
Share on other sites

Wow this is really neat, big thanks. Another question I have - what if I want to pass both filename and path to joining element as parameters? It works for the filename, so I can do something like: <xsl:param name="i_sBaseFileName" /> <xsl:variable name="oJoinData" select="document($i_sBaseFileName)" />But it doesn't work if I want to specify a path as a parameter: <xsl:param name="i_sBaseFileName" /> <xsl:param name="i_sDataPath" /> <xsl:variable name="oJoinData" select="document($i_sBaseFileName)$i_sDataPath" />The parsers throws an error. Any idea how to do this?Also, is there a way to do a template match to a variable or parameter? I.e.:<xsl:param name="i_sSrcPath" />...<xsl:template match="$i_sSrcPath">Thanks!

Link to comment
Share on other sites

When you think about variables and parameters in XSLT, think of them as strings (or numbers in some cases...) - nothing more and nothing less.Having that said, you can't have a string as an XPath expression or a match pattern now, can you?

<xsl:template match="'some string'">

would be a ridiculous thing to match. However, you may match the name() of an element, or at least it's local-name() like so:

<xsl:template match="*[local-name() = 'record']">

Thus making the following possible too:

<xsl:template match="*[local-name() = $i_sSrcPath]">

But no, you stilll won't be able to match a whole path, thus rendering the following invalid:

<xsl:template match="*[local-name() = '/records/record']">

The same thing goes for XPath expressions everywhere within the XSLT file - no, you can't do that. Not even in XSLT 2.0.What you could do though is to generate a new XSLT file that will place those strings as XPath expressions of the new file. The problem is you need some way to match the new XSLT to either the same XML or a new one. And you can't do that on the client side... at least not that easily. It's very easy on the server, and possible (but not easy) on the client with JavaScript.Just a plain (and not really useful) example of what I mean:

<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:myxsl="http://www.w3.org/1999/XSL/Transform#embed">	<xsl:namespace-alias stylesheet-prefix="myxsl" result-prefix="xsl"/>	<xsl:param name="i_sSrcPath" />	<xsl:template match="/*">		<myxsl:stylesheet version="1.0">			<myxsl:output method="text"/>			<myxsl:template match="/">				The XPath expression "<xsl:value-of select="$i_sSrcPath"/>" returned:				<myxsl:value-of select="{$i_sSrcPath}"/>			</myxsl:template>		</myxsl:stylesheet>	</xsl:template></xsl:stylesheet>

If you pass for example "/*/record[iD = 1]/title" as the value of the $i_sSrcPath parameter, the output of this stylesheet will be:

<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">	<xsl:template match="/">		The XPath expression "/*/record[ID = 1]/title" returned:		<xsl:value-of select="/*/record[ID = 1]/title"/>	</xsl:template></xsl:stylesheet>

Which is, as you can see, yet another XSLT file, this time with the new expression in mind. From here on, if you match this XSLT against that same XML, you'll get

The XPath expression "/*/record[ID = 1]/title" returned:some text

Actually, there's one way that you may be able to do it on any side... but it's not something I'd reccomend. Embedding the XML into the result file and adding a PI for the stylesheet with an ID like so:

<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:myxsl="http://www.w3.org/1999/XSL/Transform#embed">	<xsl:namespace-alias stylesheet-prefix="myxsl" result-prefix="xsl"/>	<xsl:param name="i_sSrcPath" />	<xsl:template match="/*">		<xsl:processing-instruction name="xml-stylesheet">type="text/xsl" href="#stylesheet"</xsl:processing-instruction>		<records>			<xsl:copy-of select="*"/>			<myxsl:stylesheet version="1.0" id="stylesheet">				<myxsl:output method="text"/>				<myxsl:template match="/">					The XPath expression "<xsl:value-of select="$i_sSrcPath"/>" returned:					<myxsl:value-of select="{$i_sSrcPath}"/>				</myxsl:template>			</myxsl:stylesheet>		</records>	</xsl:template></xsl:stylesheet>

This would work well in most cases, unless you happen to have another element with ID of "stylesheet" in your source document.

Link to comment
Share on other sites

Thanks yet again. Actually I indeed do all the processing on the server (ASP.NET) and was thinking about generating an XSLT stylesheet programmaticaly. But I was going to try this using .NET classes like XMLDocument. The thought of generating an XSLT using XSLT never crossed my mind and I think thats ingenious. You re a true guru :)

Link to comment
Share on other sites

Another question for the guru :)The XSL works great to copy all elements from the matching node of external file into current node. But how do I modify the line:<xsl:copy-of select="document('File.xml')/*/record[@ID = $ID]/*"/>To copy only those elements that _don't already exist_ in current node?I tried something like <xsl:copy-of select="document('File.xml')/*/record[@ID = $ID]/*[name() != name(current()/*)]"/>But it matches only first name in current elements nodeset.

Link to comment
Share on other sites

It's going to be a little more complex then that. At least without XSLT 2.0 anyway.I'm not sure (haven't tried it), but I think simply appying the templates instead of copying the nodes should pretty much do it.

<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">	<xsl:template match="/*">		<records>			<xsl:apply-templates/>		</records>	</xsl:template>	<xsl:template match="record">		<xsl:variable name="ID" select="@ID"/>		<xsl:copy>			<xsl:apply-templates select="document('File.xml')/*/record[@ID = $ID]/*[name() != name(current()/*)]"/>			<xsl:apply-templates/>		</xsl:copy>	</xsl:template>	<xsl:template match="*|@*">		<xsl:copy>			<xsl:apply-templates/>		</xsl:copy>	</xsl:template></xsl:stylesheet>

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...