I am running a batch file using msxsl to take an input xml file like below

<ns1:invoicedata>
<data1>1</data1>
<data2>2</data2>
</ns1:invoicedata>

I need to copy this original output and replace the values with the input file values

<ns2:invoiceoutput>
<data3>3</data3>
<data4>4</data4>
<data5>5</data5>
</ns2:invoiceoutput>

so that it ends up with the following data in it

<ns2:invoiceoutput>
<data3>1</data3>
<data4>2</data4>
<data5>5</data5>
</ns2:invoiceoutput>

I've tried template matching... value-of select= statements, choose, etc... but I can't
seem to get the desired output. Just started using xsl and it seems doable... anyone?
Thanks!

Dani AI

Generated

Two clean patterns solve this kind of task (both appear in the thread): build the target output directly from the invoice input (what illustrated), or treat the “stock” output file as the template and patch just the elements that need replacing. Patching is usually the better choice when the stock file must be kept exactly (namespaces, attributes, ordering, comments). msxsl.exe is a small Microsoft command-line XSLT processor that uses MSXML (XSLT 1.0), so loading a second XML with XSLT’s document() and doing an identity-copy with a targeted replacement is available and convenient. (flylib.com)

Practical recipe (patch-the-template):

  • Add the same namespace declarations (tns, dp, etc.) to the stylesheet that the stock output uses — pattern matching is namespace-sensitive. (dpxslfaq.xml.com)
  • Load the stock output with document('invoice_output.xml') into a variable and apply-templates / identity-copy to that node tree. (learn.microsoft.com)
  • Add a narrow template that matches the element(s) to replace (for example dp:Identifier) and emit the invoice value (e.g. EInvoiceData/CustOrdNumber) instead of the original text. Relative URIs passed to document() are resolved against the stylesheet’s base URI, so use absolute paths or ensure the stylesheet has a stable location. (stackoverflow.com)

Minimal example (shows intent — keep namespaces, file names and exact XPaths to your documents):

<xsl:variable name="tpl" select="document('invoice_output.xml')/tns:InvoiceNotification"/>

<xsl:template match="/">
  <xsl:copy-of select="$tpl"/>
</xsl:template>

<xsl:template match="dp:Identifier">
  <xsl:copy>
    <xsl:value-of select="/EInvoiceData/CustOrdNumber"/>
  </xsl:copy>
</xsl:template>

Troubleshooting notes: MSXML 6 can block the document() function by default (AllowDocumentFunction), so an “Access denied” error can appear — either pick an MSXML version that allows it or enable the property programmatically. msxsl has options to choose the MSXML version and control external-definition parsing if needed. If document() cannot be used, the DTD-entity approach shown by is a workable fallback but depends on entity resolution settings. (learn.microsoft.com)

Recommended Answers

All 5 Replies

I'm confused as to what you're trying to do. Do you have input documents? How do you know which invoice in one document is supposed to be paired with an invoice in another document? Also XSLT 1.0 or 2.0? Please post a better description of the problem with your sample inputs IN CODEBOXES and I'll take a look. This is certainly doable in xslt.

Thanks for the reply... I am trying to take an input file that has xml order data and use msxsl.exe (1.0 pretty old, probably ) and run it as a batch file to modify an output message file. Something like this...

msxsl.exe invoice.xml adddata.xsl -o test_out.xml

So, my input is the invoice.xml, I run the transformation with adddata.xsl to produce the output test_out.xml. All I'm trying to do is take the data from the input file and modify specific elements of the output file.

I copied the original output file with the following:

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

Then, I tried using template matches to select the elements from the input file whose data I want to modify the output file with

<xsl:template match="/">
  <xsl:element name="InvoiceNotification">
      <xsl:apply-templates select="EInvoiceData/CustOrdNumber"/>
   </xsl:element>
</xsl:template>

<xsl:template match="EInvoiceData/CustOrdNumber">
   <xsl:element name="tns:PurchaseOrderNumber/dp:Identifier" >
      <xsl:value-of select="."/>
   </xsl:element>
</xsl:template>

This is a one for one transformation.... one input xml file is created per invoice, so all I need to do is get this input file data into the stock output file and I should have what I need.

Sorry about format... I haven't posted much development code... but here is a snippet of the input xml file

<EInvoiceData>
<InvoiceDate>1070108</InvoiceDate>
<CustomerNumber>4638501</CustomerNumber>
<InvoiceNumber>59925</InvoiceNumber>
<CustOrdNumber>R11-9838021</CustOrdNumber>
</EInvoiceData>

Here is the output xml I need to transform :

<tns:InvoiceNotification>
<tns:Invoice>
<tns:BillFrom>
<tns:PurchaseOrderNumber>
<dp:Identifier>XXXXXXX</dp:Identifier>
</tns:PurchaseOrderNumber>
</tns:BillFrom>
</tns:Invoice>
</tns:InvoiceNotification>

I would be trying to find a match in the input for CustOrdNumber and get that into the output files PurchaseOrderNumber/Identifier element.

I hope that clears it up a bit...

Thanks!

Here ya go. Since you didn't give me namespace definitions for the tns: and dp: namespace I just made it up. Make sure you change those to whatever namesapce that you need in the transformation.

Input

<EInvoiceData>
	<InvoiceDate>1070108</InvoiceDate>
	<CustomerNumber>4638501</CustomerNumber>
	<InvoiceNumber>59925</InvoiceNumber>
	<CustOrdNumber>R11-9838021</CustOrdNumber>
</EInvoiceData>

XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:dp="SOMETHINGELSE" xmlns:tns="SOMENAMESPACE" >

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

	<xsl:template match="EInvoiceData" >
		<tns:InvoiceNotification>
			<tns:Invoice>
				<tns:BillFrom>
					<xsl:apply-templates select="CustOrdNumber" />
				</tns:BillFrom>
			</tns:Invoice>
		</tns:InvoiceNotification>			
	</xsl:template>

	<xsl:template match="CustOrdNumber" >
		<tns:PurchaseOrderNumber>
			<dp:Identifier>
				<xsl:value-of 	select="." />
			</dp:Identifier>
		</tns:PurchaseOrderNumber>
	</xsl:template>	
</xsl:stylesheet>

Output

<tns:InvoiceNotification xmlns:dp="SOMETHINGELSE" xmlns:tns="SOMENAMESPACE">
	<tns:Invoice>
		<tns:BillFrom>
			<tns:PurchaseOrderNumber>
				<dp:Identifier>R11-9838021</dp:Identifier>
			</tns:PurchaseOrderNumber>
		</tns:BillFrom>
	</tns:Invoice>
</tns:InvoiceNotification>

i use the way with intern DTD
so i make a xmltogether to put the two xml in one xml file

<?xml version="1.0"?>
<!DOCTYPE root[
  <!ENTITY xml1 SYSTEM "invoicedata.xml">
  <!ENTITY xml2 SYSTEM "invoiceoutput.xml">  
]>
<root>
  &xml1;
  &xml2;  
</root>

so you can use xlt

use namespace and exclude namespace1

you found the documentroot in the together xmlfile
store in variable datei1 the value all node you wanted to compare

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ns2="" xmlns:ns1="" exclude-result-prefixes="ns1">
	<xsl:output method="xml" indent="yes"/>
	<xsl:variable name="datei1" select="//root/ns1:invoicedata/child::*[contains(local-name(.),'data')]"/>
	<xsl:template match="/">
		<ns2:invoiceoutput xmlns:ns2="">
			<xsl:apply-templates select="root"/>
		</ns2:invoiceoutput>
	</xsl:template>
	<xsl:template match="root">
		<xsl:apply-templates select="ns2:invoiceoutput"/>
	</xsl:template>
	<xsl:template match="ns2:invoiceoutput">
		<xsl:apply-templates select="child::*[contains(local-name(.),'data')]"/>
	</xsl:template>
	<xsl:template match="*">
		<xsl:variable name="dataname" select="local-name(.)"/>
		<xsl:variable name="pos" select="position()"/>
		<xsl:choose>
			<xsl:when test="$datei1[$pos]">
				<xsl:element name="{$dataname}">
					<xsl:value-of select="$datei1[$pos]"/>
				</xsl:element>
			</xsl:when>
			<xsl:otherwise>
				<xsl:element name="{$dataname}">
					<xsl:value-of select="."/>
				</xsl:element>
			</xsl:otherwise>
		</xsl:choose>
	</xsl:template>
</xsl:stylesheet>

result

<?xml version='1.0' ?>
<ns2:invoiceoutput xmlns:ns2="">
  <data3>1</data3>
  <data4>2</data4>
  <data5>5</data5>
</ns2:invoiceoutput>

Thanks for the helpful input.... I knew I was almost on track... but couldn't quite get there. Now my batch file is transforming like a champ!

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.