Application using saxonHE (9.2.1.1) api to handle XSLT (v2.0) for multiple XML files

I have an application that uses SAXONHE 9.2.1.1 api files to convert XML data to plain text. My form has text fields for

  • XMLInput_FilePath
  • XSLT_FilePath
  • TextOutput_FilePath

In the case of okButton_Click () of my form, I have the following:

private void okButton_Click(object sender, EventArgs e) { FileStream xsltTransform_FileStream = File.Open(xsltTransform_FilePath.Text, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); FileStream xmlInput_FileStream = File.Open(xmlInput_FilePath.Text, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); XmlTextReader modelFileXML = new XmlTextReader(xmlInput_FileStream); modelFileXML.XmlResolver = null; Processor processor = new Processor(); XdmNode input = processor.NewDocumentBuilder().Build(modelFileXML); XsltTransformer xsltTransformer = processor.NewXsltCompiler().Compile(xsltTransform_FileStream).Load(); xsltTransformer.InputXmlResolver = null; xsltTransformer.InitialContextNode = input; Serializer serializer = new Serializer(); serializer.SetOutputFile(writeFile); xsltTransformer.Run(serializer); xsltTransform_FileStream.Close(); modelFileStream.Close(); } 

In the context of my XMLInput file, there is a link to the data in another XML file - see below:

XML:

 <XMLInput_File Name="XMLInput_File"> <Subsystem Name="Subsystem"> <Requirements Name="Requirement_1"> <Rows> <Path Text="XMLInput2_File:/XMLInput2_File/Subsystem_1/Field_1" /> </Rows> <Rows> <Path Text="XMLInput2_File:/XMLInput2_File/Subsystem_1/Field_2" /> </Rows> </Requirements> <Requirements Name="Requirement_2"> <Rows> <Path Text="XMLInput2_File:/XMLInput2_File/Subsystem_1/Field_3" /> </Rows> <Rows> <Path Text="XMLInput2_File:/XMLInput2_File/Subsystem_2/Field_1" /> </Rows> </Requirements> </Subsystem> </XMLInput_File> 

The Text attribute is the place where the external path of the XML file is stored. In the above example, the XML file name will be “XMLInput2_File.xml”.

xml2:

 <XMLInput2_File Name="XMLInput2_File"> <Subsystem Name="Subsystem_1"> <Fields Name="Field_1"> S1_Field_One </Fields> <Fields Name="Field_2"> S1_Field_Two </Fields> <Fields Name="Field_3"> S1_Field_Three </Fields> </Subsystem> <Subsystem Name="Subsystem_2"> <Fields Name="Field_1"> S2_Field_One </Fields> <Fields Name="Field_2"> S2_Field_Two </Fields> <Fields Name="Field_3"> S2_Field_Three </Fields> </Subsystem> </XMLInput2_File> 

XSLT:

  <xsl:template match="/"> <xsl:for-each select ="//Rows/Path"> <xsl:variable name ="interfaceData" select ="@Text"/> <xsl:variable name ="_intfModelName" select ="substring-before(@Text,':/')"/> <xsl:variable name ="_intfFileName" select ="concat('../../OtherXMLFiles/',$_intfModelName,'.xml')"/> <xsl:apply-templates select ="document($_intfFileName)/*[@Name=$_intfModelName]/*"> </xsl:apply-templates> </xsl:for-each> </xsl:template> 

I am using Microsoft Visual Studion 2008 Professional Edition to test my conversion, and the script described above works just as it should - document () refers to an external file more specifically. However, when I use my C # Winform application and saxon api calls, my output file contains empty data (empty lines).

After several tests and searching on the Internet, I came to the conclusion that the relative path [in my XSLT] does not apply as it should. It seems that saxon api is processing the document () function from the location of the transform.exe file and not the input file (which I would prefer).

I am trying to make more internet requests on this issue, and I am confused about whether the problem is in my XSLT file or in saxon api calls in the okButton_Click () event. Also, I was on the saxon site and documentation for reference, but to no avail.

+6
source share
2 answers

In your XSLT use

  <xsl:apply-templates select ="document($_intfFileName, /)/*[@Name=$_intfModelName]/*"> 

if you want the relative URL to be resolved relative to the input document (and not the stylesheet).

+1
source

After posting my request to the Saxon help mailing list, Michael Kay from Saxony helped me with this. The error was in my saxon api calls (C # applicaiton). Below is the updated code:

 private void okButton_Click(object sender, EventArgs e) { Processor processor = new Processor(); XdmNode input = processor.NewDocumentBuilder().Build(new Uri(inputFile)); XsltCompiler compiler = processor.NewXsltCompiler(); compiler.BaseUri = new Uri(inputFile); XsltTransformer xsltTransformer = compiler.Compile(new Uri(transformFileTextBox.Text)).Load(); xsltTransformer.InitialContextNode = input; Serializer serializer = new Serializer(); serializer.SetOutputFile(writeFile); xsltTransformer.Run(serializer); xsltTransform_FileStream.Close(); modelFileStream.Close(); } 

"The problem is that you call document () to resolve the relative URI whose base URI is unknown. Because you use a substring before calculating the relative URI, the argument is a string, so the base URI to be used is the stylesheet. The base URI of the stylesheet is unknown since you built a stylesheet from the stream; in this situation, Saxon defaults to the current working directory. You can set the base URI using the XsltCompiler.BaseUri Property. "- Michelle Kai

So here is what I did

  • removed both FileStream variables
  • created an XsltCompiler object and installed BaseUri in the input file
  • replaced the signature of FileStream with the signature of 'new Uri (filename)
  • deleted this line of code 'xsltTransformer.InputXmlResolver = null;'

My conclusion is as it should be. Many thanks to Michael Kay of Saxony for his help in resolving this. - Lorenz

0
source

Source: https://habr.com/ru/post/913047/


All Articles