Merge two xml file nodes with same node value - c#

I want to merge nodes of two xml files in C# or XSLT. If the Path value of Method nodes of two different xml files are same. The two Method nodes should be merged as one in the output.
Example:
File1:
<Methods>
<Method>
<ID>1234</ID>
<Name>manager</Name>
<Path>path1</Path>
</Method>
</Methods>
File2:
<Methods>
<Method>
<Path>path1</Path>
<Description>text</Description>
</Method>
</Methods>
Output:
<Methods>
<Method>
<ID>1234</ID>
<Name>manager</Name>
<Description>text</Description>
</Method>
</Methods>

Try it this way?
XSLT 1.0
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:variable name="file2" select="document('file2.xml')" />
<xsl:key name="method-by-path" match="Method" use="Path" />
<!-- identity transform -->
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Method">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
<xsl:variable name="path" select="Path"/>
<!-- switch context to the other file -->
<xsl:for-each select="$file2">
<xsl:copy-of select="key('method-by-path', $path)/*[not(self::Path)]" />
</xsl:for-each>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
Note: this does not check for duplicate nodes.

LINQ to XML is quite effective. Try
var xml1 = XDocument.Load("File1.xml");
var xml2 = XDocument.Load("File2.xml");
foreach (XElement metNode in xml1.Descendants("ID"))
{
metNode.AddAfterSelf(xml2.Descendants("Path").Where(ele => ele.Value.Equals(metNode.Parent.Element("Path").Value)).FirstOrDefault().Parent.Element("Description"));
}

Related

InnerXML Replace not working with XMLDOCUMENT

I have an XML file with :
<IMG0></IMG0>
<IMG1></IMG1>
<IMG2></IMG2>
It represents up to 10 images.
I try to delete the number for having :
<IMG></IMG>
<IMG></IMG>
<IMG></IMG>
I make :
for (int l = 0; l <= 10; l++)
{
doc.InnerXml.Replace("IMG" + l, "IMG");
}
"doc" is an XMLDocument.
But the node doesn't change.
What can I do ?
By using XSLT and Identity Transform pattern.
The 2nd template will find any XML element that starts with 'IMG' and transform it to just 'IMG' while keeping everyhing else as-is intact.
XML
<?xml version="1.0" encoding="UTF-8"?>
<root>
<Betalningsmottagares_x0020_postnr>401 01</Betalningsmottagares_x0020_postnr>
<Betalningsmottagares_x0020_postort>Göteborg</Betalningsmottagares_x0020_postort>
<IMG0>10</IMG0>
<IMG1>20</IMG1>
<IMG2>30</IMG2>
</root>
XSLT
<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" encoding="utf-8" indent="yes" omit-xml-declaration="no"/>
<xsl:strip-space elements="*"/>
<xsl:template match="node() | #*">
<xsl:copy>
<xsl:apply-templates select="node() | #*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="*[starts-with(local-name(), 'IMG')]">
<IMG>
<xsl:value-of select="."/>
</IMG>
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>
Output
<?xml version='1.0' encoding='utf-8' ?>
<root>
<Betalningsmottagares_x0020_postnr>401 01</Betalningsmottagares_x0020_postnr>
<Betalningsmottagares_x0020_postort>Göteborg</Betalningsmottagares_x0020_postort>
<IMG>10</IMG>10
<IMG>20</IMG>20
<IMG>30</IMG>30
</root>

c# XDocument : Check if particular node name exists , if not add

I have below node which needs to be added in xslt if not exists :-
<xsl:template name="URLSpliter">
<xsl:param name="url" />
<xsl:variable name="splitURL" select="substring-after($url, '/')" />
<xsl:if test="contains($splitURL, '/')">
<!--To call the template recursively-->
<xsl:call-template name="URLSpliter">
<xsl:with-param name="url" select="$splitURL" />
</xsl:call-template>
</xsl:if>
<xsl:if test="not(contains($splitURL, '/'))">
<xsl:value-of select="$splitURL" />
</xsl:if>
</xsl:template>
For this, first I need to check if it exists or not ?-
I have checked it through -
IEnumerable<XElement> xElements = from xmlAuthor in doc.Descendants()
let xElement = xmlAuthor.Element("URLSpliter")
where xElement != null
select xmlAuthor;
var IsUrlSplitterExists= xElements.Any();
if(IsUrlSplitterExists)
{
}
1.I want to know if its correct way or not?
If not exists (element [name="URLSpliter"]) then needs to add.
How can I add it as a first node of xslt?
To select such elements in the XSLT namespace with LINQ to XML you would use
XNamespace xsl = "http://www.w3.org/1999/XSL/Transform";
if (!doc.Root.Elements(xsl + "template").Where(t => (string)t.Attribute("name") == "URLSplitter").Any()) {
doc.Root.AddFirst(new XElement(xsl + "template", new XAttribute("name", "URLSplitter"), ...))
}
Of course, as XSLT is XML, you might as well use XSLT to manipulate your XSLT:
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:axsl="http://www.w3.org/1999/XSL/Transform-alias"
exclude-result-prefixes="axsl"
version="1.0">
<xsl:namespace-alias stylesheet-prefix="axsl" result-prefix="xsl"/>
<xsl:output indent="yes"/>
<xsl:param name="new-template">
<axsl:template name="URLSpliter">
<axsl:param name="url" />
<axsl:variable name="splitURL" select="substring-after($url, '/')" />
<axsl:if test="contains($splitURL, '/')">
<!--To call the template recursively-->
<axsl:call-template name="URLSpliter">
<axsl:with-param name="url" select="$splitURL" />
</axsl:call-template>
</axsl:if>
<axsl:if test="not(contains($splitURL, '/'))">
<axsl:value-of select="$splitURL" />
</axsl:if>
</axsl:template>
</xsl:param>
<xsl:template match="#* | node()">
<xsl:copy>
<xsl:apply-templates select="#* | node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xsl:transform[not(xsl:template[#name = 'URLSplitter'])] | xsl:stylesheet[not(xsl:template[#name = 'URLSplitter'])]">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:copy-of select="$new-template"/>
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
https://xsltfiddle.liberty-development.net/94rmq5T

XSLT Conditional Template transformation

I am having one XMl, I want to remove some portion of it based upon a condition.
the XML is as follows :
<?xml version="1.0" encoding="UTF-8"?>
<Root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Header>
<MessageID>0a9d3ba5-9025-4e24-a0de-2403a0c0919b</MessageID>
<MessageDate>2015-04-10</MessageDate>
<PPMVersion>6.0.0.0</PPMVersion>
<SchemaVersion>1.0</SchemaVersion>
</Header>
<Package xsi:type="Ritter_Sales_Template" Path="/Package/Product/Launch_Entity" BusinessID="010170" ID="a13b6861-bb32-47fa-8ef4-f0ddcc0bc134">
<Name>MOTO_pack</Name>
<Category_ID Product_Line_ID="5">5</Category_ID>
<Effective_Start_Date>2015-02-23</Effective_Start_Date>
<Available_Start_Date>2015-02-23</Available_Start_Date>
<Element_Guid>a13b6861-bb32-47fa-8ef4-f0ddcc0bc134</Element_Guid>
<Element_Type_Guid>f7e69de1-23e8-4a66-b57b-d849e989e1ce</Element_Type_Guid>
<Business_ID>010170</Business_ID>
<Product_Name>MOTO_pack</Product_Name>
<Product_To_Product xsi:type="Product_Relation" ID="38224fc0-1a65-4b8e-9985-2277c99bbebf" Pattern="Relation_Entity">
<Association_Start_Date>2015-02-23</Association_Start_Date>
<Association_End_Date>2015-04-15</Association_End_Date>
<Product xsi:type="New_Test_template" Path="/Component/Product/Launch_Entity" BusinessID="009154" ID="da80790a-0523-472c-89b0-208d919fcf73">
<Name>idea_test</Name>
<Category_ID Product_Line_ID="5">8</Category_ID>
<Effective_Start_Date>2015-02-19</Effective_Start_Date>
<Available_Start_Date>2015-02-19</Available_Start_Date>
<Element_Guid>da80790a-0523-472c-89b0-208d919fcf73</Element_Guid>
<Element_Type_Guid>0a7fb9ea-95c4-4eed-a716-ca588f8dcae8</Element_Type_Guid>
<Business_ID>009154</Business_ID>
<Product_Name>idea_test</Product_Name>
<charge_role xsi:type="Lookup_Charge_Role" ID="6920b661-63de-45a0-94d4-900a4a873632" Pattern="Lookup">
<Name>Recipient</Name>
<Description>Recipient</Description>
</charge_role>
</Product>
</Product_To_Product>
<Product_To_Product xsi:type="Product_Relation" ID="eae5a5fd-1a27-4832-bdbe-c77907a77538" Pattern="Relation_Entity">
<Association_Start_Date>2015-02-23</Association_Start_Date>
<Association_End_Date>2015-04-10</Association_End_Date>
<Product xsi:type="Component_Template" Path="/Product_Component/Component/Product/Launch_Entity" BusinessID="010169" ID="9f4a5a5b-1cb4-463b-b211-98f08fec2ce7">
<Name>Moto-g</Name>
<Category_ID Product_Line_ID="5">8</Category_ID>
<Effective_Start_Date>2015-02-22</Effective_Start_Date>
<Available_Start_Date>2015-02-23</Available_Start_Date>
<Element_Guid>9f4a5a5b-1cb4-463b-b211-98f08fec2ce7</Element_Guid>
<Element_Type_Guid>26c0d089-259b-444b-842a-e42d21dea778</Element_Type_Guid>
<Business_ID>010169</Business_ID>
<Product_Name>Moto-g</Product_Name>
<Product_To_Product xsi:type="Product_Relation" ID="96ef93fd-ba40-4628-b22a-92c03e606a89" Pattern="Relation_Entity">
<Association_Start_Date>2015-02-23</Association_Start_Date>
<Product xsi:type="New_Test_template" Path="/Component/Product/Launch_Entity" BusinessID="009154" ID="da80790a-0523-472c-89b0-208d919fcf73">
<Name>idea_test</Name>
<Category_ID Product_Line_ID="5">8</Category_ID>
<Effective_Start_Date>2015-02-19</Effective_Start_Date>
<Available_Start_Date>2015-02-19</Available_Start_Date>
<Element_Guid>da80790a-0523-472c-89b0-208d919fcf73</Element_Guid>
<Element_Type_Guid>0a7fb9ea-95c4-4eed-a716-ca588f8dcae8</Element_Type_Guid>
<Business_ID>009154</Business_ID>
<Product_Name>idea_test</Product_Name>
<charge_role xsi:type="Lookup_Charge_Role" ID="6920b661-63de-45a0-94d4-900a4a873632" Pattern="Lookup">
<Name>Recipient</Name>
<Description>Recipient</Description>
</charge_role>
</Product>
</Product_To_Product>
</Product>
</Product_To_Product>
</Package>
</Root>
in here, i want to remove the whole Product_To_Product element if the Association_End_Date with any product is past dated. for that i have written following code in which i am comparing the dates through a C# function, but i am still unable to remove the entity. please suggest where i am going wrong.
XSLT code :
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:msxsl
="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" xmlns:cs
="urn:cs">
<xsl:output method="xml" version="1.0" encoding="UTF‐8" indent="yes"/>
<msxsl:script language="C#" implements-prefix="cs">
<![CDATA[public static long compareDate(long Asso_End_Date)
{
DateTime date1 = new DateTime(Asso_End_Date);
DateTime date2 = DateTime.Now;
long result;
result = date2.Date.CompareTo(date1.Date);
return(result);
}]]>
</msxsl:script>
<!-- TEMPLATE #1 -->
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<!-- TEMPLATE #2 -->
<xsl:template name="mode1">
<xsl:apply-templates select="Product_To_Product[Association_End_Date]"/>
</xsl:template>
<xsl:template name="mode2">
<xsl:variable name="date"/><xsl:value-of select="Product_To_Product[Association_End_Date]"/>
<xsl:choose>
<xsl:when test="cs:compareDate($date) > 0">
<xsl:call-template name='mode1'/>
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
There are a number of issues with your XSLT. First, as mentioned by Lingamurthy CS in comments, you have a named template, called mode2, but nothing is calling that template, so it never gets used. It should really be changed to a template match
<xsl:template match="Product_To_Product[Association_End_Date]">
Secondly, you have a problem with how you define your date variable.
<xsl:variable name="date"/>
In other words, you don't define it. It is effectively empty. The xsl:value-of that follows the variable declaration won't affect it at all. You should define it like so
<xsl:variable name="date" select="Association_End_Date"/>
Thirdly, there is a problem with your C# function itself
public static long compareDate(long Asso_End_Date)
The parameter has type long, but you are not passing in long, but a string. The function should really look like this, so it can then parse the string
public static long compareDate(string Asso_End_Date)
{
DateTime date1 = DateTime.Parse(Asso_End_Date);
DateTime date2 = DateTime.Now;
long result;
result = date2.Date.CompareTo(date1.Date);
return(result)
}
Also, you probably need to replace > with < when checking the results of the call.
Try this XSLT (also note how the mode1 template is not needed.
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" xmlns:cs="urn:cs">
<xsl:output method="xml" version="1.0" encoding="UTF‐8" indent="yes"/>
<msxsl:script language="C#" implements-prefix="cs">
<![CDATA[public static long compareDate(string Asso_End_Date)
{
DateTime date1 = DateTime.Parse(Asso_End_Date);
DateTime date2 = DateTime.Now;
long result;
result = date2.Date.CompareTo(date1.Date);
return(result);
}]]>
</msxsl:script>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Product_To_Product[Association_End_Date]">
<xsl:variable name="date" select="Association_End_Date"/>
<xsl:choose>
<xsl:when test="cs:compareDate($date) <= 0">
<xsl:call-template name="identity" />
</xsl:when>
<xsl:otherwise>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
Or better still, put the condition in the template match, to simplify it like so:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:fo="http://www.w3.org/1999/XSL/Format" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" xmlns:cs="urn:cs">
<xsl:output method="xml" version="1.0" encoding="UTF‐8" indent="yes"/>
<msxsl:script language="C#" implements-prefix="cs">
<![CDATA[public static long compareDate(string Asso_End_Date)
{
DateTime date1 = DateTime.Parse(Asso_End_Date);
DateTime date2 = DateTime.Now;
long result;
result = date2.Date.CompareTo(date1.Date);
return(result);
}]]>
</msxsl:script>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Product_To_Product[cs:compareDate(Association_End_Date) > 0]" />
</xsl:stylesheet>

"Execution of the 'document()' function was prohibited." where EnableDocumentFunction set to true?

I am getting an intermittent System.Xml.Xsl.XslTransformException exception in our production environment when attempting an xslt transform, unfortunately I cannot replicate this in the development environment.
The exception spits out further details:
Execution of the 'document()' function was prohibited. Use the
XsltSettings.EnableDocumentFunction property to enable it. An error
occurred at C:\path\to\file\CDS.xsl(16,3).
However the EnableDocumentFunction property is set to true in the processing code:
private void Transform()
{
var keepTrying = true;
var tryCount = 0;
const int maxRetrys = 3;
while (keepTrying)
{
try
{
var xmlResolver = new XmlUrlResolver();
using (var xmlFile = new XmlNodeReader(_xDoc))
{
var settings = new XmlReaderSettings
{
XmlResolver = xmlResolver,
ProhibitDtd = false,
DtdProcessing = DtdProcessing.Ignore
};
using (var xsl = XmlReader.Create(_xslPath, settings))
{
var xslt = new XslCompiledTransform(true);
xslt.Load(xsl, new XsltSettings { EnableDocumentFunction = true }, xmlResolver);
var sb = new StringBuilder();
using (var writer = XmlWriter.Create(sb, xslt.OutputSettings))
{
xslt.Transform(xmlFile, null, writer, xmlResolver); // errors out here.
}
var xhtml = sb.ToString();
_transformedXml = xhtml;
_isTransformed = true;
xsl.Close();
}
}
keepTrying = false;
}
catch (System.Xml.Xsl.XsltException ex)
{
ExceptionPolicy.HandleException(ex, "ExceptionLogging");
tryCount++;
if (tryCount > maxRetrys)
{
keepTrying = false;
throw;
}
}
}
}
The xslt file is provided by a third party and automatically updated, so rewriting it is not an option. Here is the top of it, slightly modified for privacy reasons:
<?xml version="1.0"?>
<!--
Interaction_550.xsl : 20110916
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:example="http://www.example.com" version="1.0">
<xsl:param name="D2DSeverityFilter"></xsl:param>
<xsl:param name="D2HSeverityFilter"></xsl:param>
<xsl:param name="DocumentationFilter"></xsl:param>
<xsl:output method="html"/>
<xsl:key name="d2d_sev_level-lookup" match="example:d2d_sev_level" use="#name"/>
<xsl:key name="d2h_sev_level-lookup" match="example:d2h_sev_level" use="#name"/>
<xsl:key name="d2l_sev_level-lookup" match="example:d2l_sev_level" use="#name"/>
<xsl:key name="preg_cat-lookup" match="example:preg_cat" use="#cat"/>
<xsl:key name="doc_level-lookup" match="example:doc_level" use="#name"/>
<xsl:variable name="d2d_sev_level-top" select="document('')/*/example:d2d_sev_levels"/>
<xsl:variable name="d2h_sev_level-top" select="document('')/*/example:d2h_sev_levels"/>
<xsl:variable name="d2l_sev_level-top" select="document('')/*/example:d2l_sev_levels"/>
<xsl:variable name="doc_level-top" select="document('')/*/example:doc_levels"/>
<xsl:variable name="preg_cat-top" select="document('')/*/example:preg_cats"/>
<xsl:template match="/">
<head>
<style type="text/css">
body {
font-family : arial,sans serif,helvetica;
}
...
How can I:
fix this so that it does not happen at all?
failing that, how could I go about replicating this in dev?
Alternatively with MS XslCompiledTransform class you can use a XsltSettings class to avoid this error as it described by the error itself. from MSDN
Here is a general way to get rid of the document('')/*/someName expressions:
The original transformation is processed with a special transformation that generates an equivalent transformation that doesn't contain the document('') function call.
Then you just need to use the generated transformation.
Example:
This transformation (lets name it tA), containing document(''):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my" >
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<my:paramDoc1>
<x>123</x>
<y>37</y>
</my:paramDoc1>
<my:paramDoc2>
<x>456</x>
<y>79</y>
</my:paramDoc2>
<xsl:variable name="vpDoc1" select="document('')/*/my:paramDoc1"/>
<xsl:variable name="vpDoc2" select="document('')/*/my:paramDoc2"/>
<xsl:template match="/*">
<xsl:value-of select="$vpDoc1/x"/>
<xsl:text>|</xsl:text>
<xsl:value-of select="$vpDoc2/y"/>
</xsl:template>
</xsl:stylesheet>
when applied on any document, produces:
123|79
Now we process the above transformation with this one (tGen):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:x="a:a" exclude-result-prefixes="x">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:strip-space elements="*"/>
<xsl:namespace-alias stylesheet-prefix="x"
result-prefix="xsl"/>
<xsl:variable name="vApos">'</xsl:variable>
<xsl:variable name="vSelectPrefix" select=
"concat('document(', $vApos,$vApos, ')/*/')"/>
<xsl:template match="node()|#*" name="identity">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="xsl:stylesheet">
<x:stylesheet xmlns:ext="http://exslt.org/common">
<xsl:copy-of select="#*"/>
<xsl:apply-templates/>
</x:stylesheet>
</xsl:template>
<xsl:template match="xsl:variable">
<xsl:variable name="vSelattr" select="#select"/>
<xsl:choose>
<xsl:when test="not(starts-with(#select, $vSelectPrefix))">
<xsl:call-template name="identity"/>
</xsl:when>
<xsl:otherwise>
<x:variable name="vrtf{#name}">
<xsl:copy-of select=
"/*/*[name()
= substring-after($vSelattr, $vSelectPrefix)
]"/>
</x:variable>
<x:variable name="{#name}" select=
"ext:node-set($vrtf{#name})/*"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
The result is a new transformation (tRes), which doesn't contain expressions like document(''):
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:ext="http://exslt.org/common">
<xsl:output omit-xml-declaration="yes"
indent="yes" xmlns:my="my:my" />
<xsl:strip-space elements="*" xmlns:my="my:my" />
<my:paramDoc1 xmlns:my="my:my">
<x>123</x>
<y>37</y>
</my:paramDoc1>
<my:paramDoc2 xmlns:my="my:my">
<x>456</x>
<y>79</y>
</my:paramDoc2>
<xsl:variable name="vrtfvpDoc1">
<my:paramDoc1 xmlns:my="my:my">
<x>123</x>
<y>37</y>
</my:paramDoc1>
</xsl:variable>
<xsl:variable name="vpDoc1" select="ext:node-set($vrtfvpDoc1)/*" />
<xsl:variable name="vrtfvpDoc2">
<my:paramDoc2 xmlns:my="my:my">
<x>456</x>
<y>79</y>
</my:paramDoc2>
</xsl:variable>
<xsl:variable name="vpDoc2" select="ext:node-set($vrtfvpDoc2)/*" />
<xsl:template match="/*" xmlns:my="my:my">
<xsl:value-of select="$vpDoc1/x" />
<xsl:text>|</xsl:text>
<xsl:value-of select="$vpDoc2/y" />
</xsl:template>
</xsl:stylesheet>
If we now apply this dynamically generated transformation (tRes) to any XML document, the result is exactly the same as when applying the original transformation (tA) to this document:
123|79
Therefore, we can use tGen to convert a transformation containing document('') expressions to an equivalent transformation that doesn't contain such expressions.

Use Muenchian grouping with a variable node-set?

I have a xsl variable who contains :
<Products>
<product>
<productId >1</productId>
<textdate>11/11/2011</textdate>
<price>200</price>
</product>
<product>
<productId >6</productId>
<textdate>11/11/2011</textdate>
<price>100</price>
</product>
<product>
<productId >1</productId>
<textdate>16/11/2011</textdate>
<price>290</price>
</product>
</Products>
I want to regroup product like this :
{ product 1 :
11/11/2011 - 200
16/11/2011 - 290 }
{ product 6
11/11/2011 - 100 }
with a simple Muenchian algorithme it's possible but I cant have a xsl:key with a match on a variable.
edit
Maybe I'm not clear,
I've this xml document mydocument.xml I try to transform :
<?xml version="1.0" encoding="utf-8" ?>
<root>
<test>somestuff</test>
</root>
My xsl must look like that :
<?xml version="1.0" encoding="utf-8" ?>
<xsl:stylesheet version="2.0" xmlns:ms="urn:schemas-microsoft-com:xslt" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
<xsl:output omit-xml-declaration="yes" method="xml" encoding="utf-8" />
<xsl:variable name="MyProductList" select="document('myresultlist.xml')" />
<xsl:key name="kProdById" match="$MyProductList/product" use="productId"/>
<xsl:template match=
"product[generate-id()
=
generate-id(key('kProdById',productId)[1])
]">
<xsl:value-of select="concat('
{product ', productId, ' :')"/>
<xsl:apply-templates mode="display"
select="key('kProdById',productId)"/>
}<xsl:text/>
</xsl:template>
<xsl:template match="product" mode="display">
<xsl:value-of select=
"concat('
', textdate, ' - ', price)"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
When I put my variable $MyProductList in a match Visual Studio give an error, and when I'm trying to force the run my web app crash.
So I cannnot do what dimitre explain me.
Thanks for helping me
This transformation uses the Muenchian grouping method in its classic form:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="text"/>
<xsl:key name="kProdById" match="product" use="productId"/>
<xsl:template match=
"product[generate-id()
=
generate-id(key('kProdById',productId)[1])
]">
<xsl:value-of select="concat('
{product ', productId, ' :')"/>
<xsl:apply-templates mode="display"
select="key('kProdById',productId)"/>
}<xsl:text/>
</xsl:template>
<xsl:template match="product" mode="display">
<xsl:value-of select=
"concat('
', textdate, ' - ', price)"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when applied on the provided XML document, the wanted, correct result is produced:
{product 1 :
11/11/2011 - 200
16/11/2011 - 290
}
{product 6 :
11/11/2011 - 100
}
UPDATE: The OP has shown more information and explained that the problem (in his code) is in these two lines:
<xsl:variable name="MyProductList" select="document('myresultlist.xml')" />
<xsl:key name="kProdById" match="$MyProductList/product" use="productId"/>
Solution: Just use:
<xsl:key name="kProdById" match="product" use="productId"/>
Whenever you want to reference the kProdById key in a key() function, make sure the wanted document is made current. In the more complicated cases this is done in XSLT 1.0 in the following way:
<!-- Temporarily switch to the document to be indexed -->
<xsl:for-each select="document('myresultlist.xml')">
<!-- Use here the key() function -->
</xsl:for-each>
<!-- Resume working with the main document here -->
Here is the same solution now applied to a document (its top-element) contained in a variable:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:my="my:my"
>
<xsl:output method="text"/>
<my:doc>
<Products>
<product>
<productId >1</productId>
<textdate>11/11/2011</textdate>
<price>200</price>
</product>
<product>
<productId >6</productId>
<textdate>11/11/2011</textdate>
<price>100</price>
</product>
<product>
<productId >1</productId>
<textdate>16/11/2011</textdate>
<price>290</price>
</product>
</Products>
</my:doc>
<xsl:variable name="vDoc" select="document('')/*/my:doc/*"/>
<xsl:key name="kProdById" match="product" use="productId"/>
<xsl:template match="/">
<xsl:apply-templates select="$vDoc"/>
</xsl:template>
<xsl:template match=
"product[generate-id()
=
generate-id(key('kProdById',productId)[1])
]">
<xsl:value-of select="concat('
{product ', productId, ' :')"/>
<xsl:apply-templates mode="display"
select="key('kProdById',productId)"/> }
<xsl:text/>
</xsl:template>
<xsl:template match="product" mode="display">
<xsl:value-of select=
"concat('
', textdate, ' - ', price)"/>
</xsl:template>
<xsl:template match="text()"/>
</xsl:stylesheet>
when this transformation is applied on any document (not used), again the wanted result is produced:
{product 1 :
11/11/2011 - 200
16/11/2011 - 290 }
{product 6 :
11/11/2011 - 100 }

Categories