Reading big xml file - xml-parsing

I need to read big xml file in Microsoft Visual FoxPro 9 SP2 desktop application.
Created cursor result which contains desired columns and convert.xsl file to transform xml to format readably by xmltocursor.
Tried
source = CreateObject('MSXML.Domdocument')
stylesheet = CreateObject('MSXML.Domdocument')
resultDoc = CreateObject('MSXML.Domdocument')
resultDoc.validateOnParse = .t.
stylesheet.load('convert.xsl')
source.load( 'bigxml.xml' )
source.transformNodeToObject(stylesheet, #resultDoc)
* Exception code=E06D7363
IF XMLToCursor(resultDoc.xml, 'result', 8192 )=0
But got
Fatal exception
Exception code=E06D7363
at line
IF XMLToCursor(resultDoc.xml, 'result', 8192 )=0
and application terminates.
How to convert big xml file to cursor ?
XSL is:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no" ?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" version="1.0" encoding="UTF-8" standalone="yes"/>
<!-- this replaces root tag with VFPData which is required from CursorToXML to work properly -->
<xsl:template match="/">
<xsl:element name="VFPData">
<xsl:apply-templates/>
</xsl:element>
</xsl:template>
<!-- this will denormalize XML data -->
<xsl:template match="/ettevotjad/ettevotja">
<xsl:element name="Document-Ariregister">
<xsl:element name="nimi">
<xsl:value-of select="nimi"/>
</xsl:element>
<xsl:element name="ariregistr">
<xsl:value-of select="ariregistri_kood"/>
</xsl:element>
<xsl:element name="kmkr_nr">
<xsl:value-of select="kmkr_nr"/>
</xsl:element>
<xsl:element name="ettevotja_">
<xsl:value-of select="ettevotja_staatus"/>
</xsl:element>
<xsl:element name="asukoht_et">
<xsl:value-of select="ettevotja_aadress/asukoht_ettevotja_aadressis"/>
</xsl:element>
<xsl:element name="asukoha_e2">
<xsl:value-of select="ettevotja_aadress/asukoha_ehak_tekstina"/>
</xsl:element>
<xsl:element name="indeks_ett">
<xsl:value-of select="ettevotja_aadress/indeks_ettevotja_aadressis"/>
</xsl:element>
</xsl:element>
</xsl:template>
<!-- to ommit nodes data -->
<xsl:template match="text()">
</xsl:template>
<!-- to work over every node -->
<xsl:template match="*">
<xsl:apply-templates/>
</xsl:template>
</xsl:stylesheet>

Here is a solution using mxml.6.0., no need for xs file:
* requires msxml.6.0! ( https://www.microsoft.com/es-es/download/details.aspx?id=3988 )
*
Parameters fileName,xPath
fileName = "D:\Data\Xml\ettevotja_rekvisiidid_2021-01-27.xml"
xPath = '/ettevotjad'
xPath = RTRIM(m.xPath,1,'/')
Private All
oxml = Createobject('msxml2.domDocument.6.0')
Wait 'loading document...' Window Nowait
With oXml As msxml.DOMDocument
.Load(Fullpath(m.fileName))
Wait Clear
If .parseError.errorCode # 0
Messagebox(.parseError.reason,16)
Return
Endif
Endwith
Close Tables All
nsteps = 500
tini = Seconds()
Set Escape On
x = 1
DO WHILE .t.
subset = oXml.selectNodes(Textmerge(m.xPath+'/*[ position() >= <<m.x>> and position() < <<m.x+m.nsteps>> ]'))
IF subset.length = 0
EXIT
ENDIF
cXml = ''
For Y = 1 To subset.Length
m.cXml = m.cXml + subset.Item(m.y-1).XML
ENDFOR
x = m.x + subset.Length
Wait Textmerge('<<m.x-1>> records << (m.x-1) / (SECONDS() - m.tini) >> records/sec ') Window nowait
cXml = '<xml encoding="windows-1252">'+m.cXml+'</xml>'
Xmltocursor(m.cXml,'xmlImport',Iif(Used('xmlImport'),4+8192,0))
ENDDO
Browse Normal Font 'consolas,8'

I like Marco's solution for at least it is in VFP. However, having big XML files like this, I would also care about performance. I tried with Marco's code and it took 80+ seconds to complete and may need some work if you really want those fields separated as in your XSL.
I tried, reading from that XML, transforming based on your XSL and writing to an SQLite database using C#. It took 7.2 seconds and those address lines were already separated.
Here is C# code if you would use anyway:
void Main()
{
string dataFile = #"d:\Andrus\bigdata.sqlite";
string constr = $"Data Source={dataFile}";
Stopwatch sw = new Stopwatch();
sw.Start();
if (!File.Exists(dataFile))
{
CreateDatabase(constr);
}
InsertData(constr, #"d:\Andrus\bigxml.xml");
sw.Stop();
sw.Dump("Duration");
}
void InsertData(string connectionString, string xmlFile)
{
using (SQLiteConnection conn = new SQLiteConnection(connectionString))
using (SQLiteCommand sql = new SQLiteCommand(#"insert into MyData
(nimi, ariregistr, asukoht_et, asukoha_e2, indeks_ett, kmkr_nr)
values
(?,?,?,?,?,?)", conn))
{
sql.Parameters.AddWithValue("nimi", "");
sql.Parameters.AddWithValue("ariregistr", 0);
sql.Parameters.AddWithValue("asukoht_et", "");
sql.Parameters.AddWithValue("asukoha_e2", "");
sql.Parameters.AddWithValue("indeks_ett", "");
sql.Parameters.AddWithValue("kmkr_nr", "");
conn.Open();
SQLiteTransaction transaction = conn.BeginTransaction();
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
using (XmlReader r = XmlReader.Create(xmlFile, settings))
{
r.MoveToContent();
r.ReadStartElement("ettevotjad");
while (r.Name == "ettevotja")
{
XElement x = (XElement)XNode.ReadFrom(r);
string nimi = (string)x.Element("nimi");
int? ariregistr = (int?)x.Element("ariregistri_kood");
string asukoht_et = (string)x.Element("ettevotja_aadress").Element("asukoht_ettevotja_aadressis");
string asukoha_e2 = (string)x.Element("ettevotja_aadress").Element("asukoha_ehak_tekstina");
string indeks_ett = (string)x.Element("ettevotja_aadress").Element("indeks_ettevotja_aadressis");
string kmkr_nr = (string)x.Element("kmkr_nr");
///
sql.Parameters["nimi"].Value = nimi;
sql.Parameters["ariregistr"].Value = ariregistr;
sql.Parameters["asukoht_et"].Value = asukoht_et;
sql.Parameters["asukoha_e2"].Value = asukoha_e2;
sql.Parameters["indeks_ett"].Value = indeks_ett;
sql.Parameters["kmkr_nr"].Value = kmkr_nr;
sql.ExecuteNonQuery();
}
r.ReadEndElement();
}
transaction.Commit();
conn.Close();
}
}
void CreateDatabase(string connectionString)
{
using (SQLiteConnection conn = new SQLiteConnection(connectionString))
using (SQLiteCommand sql = new SQLiteCommand())
{
sql.CommandText = #"create table MyData (
nimi text,
ariregistr number null,
asukoht_et text,
asukoha_e2 text,
indeks_ett text,
kmkr_nr text
)";
sql.Connection = conn;
conn.Open();
sql.ExecuteNonQuery();
conn.Close();
}
}
PS: Code is run directly inside LinqPad.

Related

XSLT 2.0: Check if string ends with a value from an array

How can I detect that a string ends with a value from a pre-determined array?
Couldn't figure it out myself from other questions on SO because they ask about a complete string match.
Something like this
<xsl:template match="card">
<xsl:variable name="word" select="k" />
<xsl:variable name="obviouslyRussianSuffixes" select="('изм', 'ия', 'ист', 'ёр', 'ца', 'фон')" />
<xsl:if test="ends-with($word, $obviouslyRussianSuffixes)">
<xsl:value-of select="$word" />
<xsl:text>
</xsl:text>
</xsl:if>
</xsl:template>
I think you want the test to be some $suffix in $obviouslyRussianSuffixes satisfies ends-with($word, $suffix).

How to compare a single value to an array using XSLT

I want to compare a single value if it's existing to an array. I have an XML input file:
<wd:Report_Entry
xmlns:wd="urn:com.workday.report/CRI_-_INTSG031_CM_011_-_PUBBS_HSA_Customer_Invoice">
<wd:CFI_ARI_PUBBS_HSA_group>
<wd:pubbsType>1</wd:pubbsType>
<wd:pubbsService>A+</wd:pubbsService>
<wd:validFrom>2019-08-01</wd:validFrom>
<wd:validTo>2100-01-01</wd:validTo>
<wd:CF_FD_PUBBS_HSA_Valid_From_Date>2019-08-01</wd:CF_FD_PUBBS_HSA_Valid_From_Date>
<wd:CF_FD_PUBBS_HSA_Valid_To_Date>2100-01-01</wd:CF_FD_PUBBS_HSA_Valid_To_Date>
</wd:CFI_ARI_PUBBS_HSA_group>
<wd:CFI_ARI_PUBBS_HSA_group>
<wd:pubbsType>1</wd:pubbsType>
<wd:pubbsService>A-</wd:pubbsService>
<wd:validFrom>2019-08-01</wd:validFrom>
<wd:validTo>2100-01-01</wd:validTo>
<wd:CF_FD_PUBBS_HSA_Valid_From_Date>2019-08-01</wd:CF_FD_PUBBS_HSA_Valid_From_Date>
<wd:CF_FD_PUBBS_HSA_Valid_To_Date>2100-01-01</wd:CF_FD_PUBBS_HSA_Valid_To_Date>
</wd:CFI_ARI_PUBBS_HSA_group>
<wd:CFI_ARI_PUBBS_HSA_group>
<wd:pubbsType>FMDAUTOPSY</wd:pubbsType>
<wd:pubbsService>FMDAUTOPSY</wd:pubbsService>
<wd:validFrom>2020-02-02</wd:validFrom>
<wd:validTo>2020-02-01</wd:validTo>
<wd:CF_FD_PUBBS_HSA_Valid_From_Date>2020-02-01</wd:CF_FD_PUBBS_HSA_Valid_From_Date>
<wd:CF_FD_PUBBS_HSA_Valid_To_Date>2100-01-01</wd:CF_FD_PUBBS_HSA_Valid_To_Date>
</wd:CFI_ARI_PUBBS_HSA_group>
<wd:CFI_ARI_PUBBS_HSA_group>
<wd:pubbsType>FMDBODYSTOR</wd:pubbsType>
<wd:pubbsService>FMDBODYSTOR</wd:pubbsService>
<wd:validFrom>2020-01-01</wd:validFrom>
<wd:validTo>2020-01-31</wd:validTo>
<wd:CF_FD_PUBBS_HSA_Valid_From_Date>2020-01-01</wd:CF_FD_PUBBS_HSA_Valid_From_Date>
<wd:CF_FD_PUBBS_HSA_Valid_To_Date>2020-01-31</wd:CF_FD_PUBBS_HSA_Valid_To_Date>
</wd:CFI_ARI_PUBBS_HSA_group>
<wd:Customer_Invoice_Lines_group>
<wd:referenceID>CUSTOMER_INVOICE_LINE-6-46</wd:referenceID>
<wd:Unit_Cost>0</wd:Unit_Cost>
<wd:Quantity>0</wd:Quantity>
<wd:Calculated_Tax_Amount>0</wd:Calculated_Tax_Amount>
<wd:CFI_AC_Merchandise_Amount>100</wd:CFI_AC_Merchandise_Amount>
<wd:Line_Item_Description>LINE ITEM DESC</wd:Line_Item_Description>
<wd:Line_Memo>LINE MEMO</wd:Line_Memo>
<wd:Transaction_Tax_Amount>0</wd:Transaction_Tax_Amount>
<wd:CFI_LRV_Sales_Item_Reference_ID>FMDAUTOPSY</wd:CFI_LRV_Sales_Item_Reference_ID>
</wd:Customer_Invoice_Lines_group>
<wd:CFI_FD_PUBBS_HSA_Current_Date>2020-02-16</wd:CFI_FD_PUBBS_HSA_Current_Date>
</wd:Report_Entry>
I want to know if the wd:CFI_LRV_Sales_Item_Reference_ID value is equal to wd:Customer_Invoice_Lines_group/wd:CFI_ARI_PUBBS_HSA_group/wd:pubbsService and wd:CFI_FD_PUBBS_HSA_Current_Date is greater than wd:CF_FD_PUBBS_HSA_Valid_To_Date and then just return a string value true if there is. I have a code like this but this doesn't work because I'm comparing a value to an object that has multiple values. Not sure how to do compare single value to an array using xslt 2 or 3.. Appreciate the help.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:mf="http://example.com/mf"
exclude-result-prefixes="#all" version="3.0"
xmlns:wd="urn:com.workday.report/CRI_-_INTSG031_CM_011_-_PUBBS_HSA_Customer_Invoice"
xmlns:d9="urn:this-stylesheet">
<xsl:output method="xml"/>
<xsl:template match="/">
<PUBBS>
<xsl:for-each select="wd:Report_Entry/wd:Customer_Invoice_Lines_group">
<xsl:if test="wd:CFI_LRV_Sales_Item_Reference_ID != ''">
<xsl:for-each select="../wd:CFI_ARI_PUBBS_HSA_group">
<xsl:if test="../wd:CFI_ARI_PUBBS_HSA_group/wd:pubbsService != ''
and wd:CFI_LRV_Sales_Item_Reference_ID = ../wd:CFI_ARI_PUBBS_HSA_group/wd:pubbsService
and xs:date(../wd:CFI_FD_PUBBS_HSA_Current_Date) > xs:date(../wd:CFI_ARI_PUBBS_HSA_group/wd:CF_FD_PUBBS_HSA_Valid_To_Date)
">
<HASINVALIDSALESITEM>
<xsl:value-of select="'true'"/>
</HASINVALIDSALESITEM>
</xsl:if>
</xsl:for-each>
</xsl:if>
</xsl:for-each>
</PUBBS>
</xsl:template>
</xsl:stylesheet>
I have tried to simplify the code by using xpath-default-namespace and by using templates; I have also assumed the two values you want to compare the two details of the various CFI_ARI_PUBBS_HSA_group to only exist once in the input document:
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xpath-default-namespace="urn:com.workday.report/CRI_-_INTSG031_CM_011_-_PUBBS_HSA_Customer_Invoice"
exclude-result-prefixes="#all"
version="3.0">
<xsl:output method="xml" indent="yes"/>
<xsl:mode on-no-match="shallow-skip"/>
<xsl:template match="/*">
<PUBBS>
<xsl:apply-templates select="CFI_ARI_PUBBS_HSA_group"/>
</PUBBS>
</xsl:template>
<xsl:template match="CFI_ARI_PUBBS_HSA_group[xs:date(//CFI_FD_PUBBS_HSA_Current_Date) > xs:date(CF_FD_PUBBS_HSA_Valid_To_Date)
and
//CFI_LRV_Sales_Item_Reference_ID = pubbsService]">
<HASINVALIDSALESITEM>true</HASINVALIDSALESITEM>
</xsl:template>
</xsl:stylesheet>
At https://xsltfiddle.liberty-development.net/jz1Q1ym that gives an empty <PUBBS/>, which seems the right result as the only CFI_ARI_PUBBS_HSA_group with pubbsService having the value FMDAUTOPSY has a CF_FD_PUBBS_HSA_Valid_To_Date of 2100-01-01 and the CFI_FD_PUBBS_HSA_Current_Date with the value 2020-02-16 is not greater than that.
It is not quite clear whether you want a single output of HASINVALIDSALESITEM even if there are several but the code could obviously adapted.

XSLT 2, Mutiple grouping by attributes and elements

I can't seem to manage grouping couple based on their MaritalStatus.
I've manage to group all the person that are in a relationship ('DeFacto', 'Married') but can't figuring out how to group them in different households.
Basically if two persons make a couple they will create a Household. The household name will belong to the first person surname in the household, the NumberOfAdults will be hardcoded to '2' and the NumberOfDependants will be summed.
The couple is linked based on the Party.Identifier and the Party.MaritalStatus.RelatedEntityRef
What i have(simplified):
<PartySegment>
<Party Type="Guarantor" PrimaryApplicant="No">
<Identifier>b8b0f908b08e</Identifier>
<Person Sex="Female" FirstHomeBuyer="No" CustomerOfLender="No">
<PersonName>
<NameTitle Value="Lady"/>
<FirstName>Clemansa</FirstName>
<Surname>Sanchez</Surname>
</PersonName>
<MaritalStatus Status="DeFacto">
<RelatedEntityRef>ea384b0bf3f5</RelatedEntityRef>
</MaritalStatus>
<NumberOfDependents>1</NumberOfDependents>
</Person>
</Party>
<Party Type="Applicant" PrimaryApplicant="Yes" ExistingCustomerID="1231">
<Identifier>bd8c65a3ad80</Identifier>
<Person Sex="Female" FirstHomeBuyer="Yes" CustomerOfLender="Yes">
<PersonName>
<NameTitle Value="Mrs"/>
<FirstName>Cheryl</FirstName>
<Surname>Bonkers</Surname>
</PersonName>
<MaritalStatus Status="Married">
<RelatedEntityRef>ee84dc9e38ec</RelatedEntityRef>
</MaritalStatus>
<NumberOfDependents>2</NumberOfDependents>
</Person>
</Party>
<Party Type="Guarantor" PrimaryApplicant="No">
<Identifier>ea384b0bf3f5</Identifier>
<Person Sex="Male" FirstHomeBuyer="No" CustomerOfLender="No">
<PersonName>
<NameTitle Value="Mr"/>
<FirstName>Greg</FirstName>
<OtherName>Morty</OtherName>
<Surname>Sanchez</Surname>
</PersonName>
<MaritalStatus Status="DeFacto">
<RelatedEntityRef>b8b0f908b08e</RelatedEntityRef>
</MaritalStatus>
<NumberOfDependents>0</NumberOfDependents>
</Person>
</Party>
<Party Type="Applicant" PrimaryApplicant="No">
<Identifier>ee84dc9e38ec</Identifier>
<Person Sex="Male" FirstHomeBuyer="No" CustomerOfLender="No">
<PersonName>
<NameTitle Value="Mr"/>
<FirstName>Mark</FirstName>
<Surname>Bonkers</Surname>
</PersonName>
<MaritalStatus Status="Married">
<RelatedEntityRef>bd8c65a3ad80</RelatedEntityRef>
</MaritalStatus>
<NumberOfDependents>0</NumberOfDependents>
</Person>
</Party>
</PartySegment>
The desire output:
<Household UniqueID="b8b0f908b08e-Household"
Name="Sanchez Household"
NumberOfAdults="2"
NumberOfDependants="1"/>
<Household UniqueID="bd8c65a3ad80-Household"
Name="Bonkers Household"
NumberOfAdults="2"
NumberOfDependants="2"/>
What i have so far(simplified): This is working for one couple because is not taking in consideration the MaritalStatus.RelatedEntityRef information
<xsl:template match="PartySegment" mode="Household_Couple">
<xsl:for-each-group select="Party[Person/MaritalStatus/#Status = ('DeFacto', 'Married')]" group-by="Person/MaritalStatus/#Status = ('DeFacto', 'Married')">
<xsl:variable name="owner_id" select="Identifier"/>
<Household UniqueID="{concat(Identifier, '-Household')}"
Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
NumberOfAdults="{'2'}"
NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">
<xsl:apply-templates select="current-group()/Person/Dependent"/>
</Household>
</xsl:for-each-group>
</xsl:template>
If you can move to XSLT 3.0 then I think a composite grouping key sort((Identifier, key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier)) of the sort of the two related identifiers can solve this:
<xsl:key name="ref" match="Party" use="Identifier"/>
<xsl:template match="PartySegment">
<xsl:for-each-group select="Party[Person/MaritalStatus/#Status = ('DeFacto', 'Married')]"
composite="yes"
group-by="sort((Identifier, key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier))">
<Household UniqueID="{concat(Identifier, '-Household')}"
Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
NumberOfAdults="{'2'}"
NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">
</Household>
</xsl:for-each-group>
</xsl:template>
I get
<Household UniqueID="b8b0f908b08e-Household"
Name="Sanchez Household"
NumberOfAdults="2"
NumberOfDependants="1"/>
<Household UniqueID="bd8c65a3ad80-Household"
Name="Bonkers Household"
NumberOfAdults="2"
NumberOfDependants="2"/>
this way with Saxon 9.8 EE inside of oXygen.
For Saxon 9.8 HE it should be possible to rewrite that as
<xsl:key name="ref" match="Party" use="Identifier"/>
<xsl:template match="PartySegment">
<xsl:for-each-group select="Party[Person/MaritalStatus/#Status = ('DeFacto', 'Married')]"
composite="yes"
group-by="Identifier | key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier">
<Household UniqueID="{concat(Identifier, '-Household')}"
Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
NumberOfAdults="{'2'}"
NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">
</Household>
</xsl:for-each-group>
</xsl:template>
For XSLT 2.0 where we don't have composite keys we would need to make sure to construct a single key with both components:
<xsl:template match="PartySegment">
<xsl:for-each-group select="Party[Person/MaritalStatus/#Status = ('DeFacto', 'Married')]"
group-by="string-join((Identifier | key('ref', Person/MaritalStatus/RelatedEntityRef)/Identifier), '|')">
<Household UniqueID="{concat(Identifier, '-Household')}"
Name="{normalize-space(concat(Person/PersonName/Surname, ' Household'))}"
NumberOfAdults="{'2'}"
NumberOfDependants="{if(Person/Dependent) then count(current-group()/Person/Dependent) else if(Person/NumberOfDependents) then sum(current-group()/Person/NumberOfDependents) else '0'}">
</Household>
</xsl:for-each-group>
</xsl:template>

SaxonApiException: The context item for axis step ./CLIENT is absent

I am trying to convert and XML to an XML using XSLT 2.0 in saxon/java. I am using a sample XML I found on stack overflow thread "Applying Muenchian grouping for a simple XML with XSLT"
However I am getting an error : XPDY0002: The context item for axis step ./CLIENT is absent.
My test XSL:
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:output omit-xml-declaration="no" indent="yes" />
<xsl:strip-space elements="*" />
<xsl:template match="CLIENTS" name="main">
<CLIENTS>
<xsl:for-each-group select="CLIENT" group-by="NAME">
<xsl:comment><xsl:value-of select="current-grouping-key()"/> </xsl:comment>
<CLIENT>
<xsl:sequence select="NAME" />
<xsl:for-each select="current-group()">
<ACCOUNT>
<xsl:sequence select="*[not(self::NAME)]" />
</ACCOUNT>
</xsl:for-each>
</CLIENT>
</xsl:for-each-group>
</CLIENTS>
</xsl:template>
</xsl:stylesheet>
My Test XML:
<CLIENTS>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT_NUMBER>1424763562761</ACCOUNT_NUMBER>
<LAST_USED>2012-10-03</LAST_USED>
<AMOUNT>5000</AMOUNT>
</CLIENT>
<CLIENT>
<NAME>John</NAME>
<ACCOUNT_NUMBER>543667543732</ACCOUNT_NUMBER>
<LAST_USED>2012-10-02</LAST_USED>
<AMOUNT>10000</AMOUNT>
</CLIENT>
</CLIENTS>
My Java ( which works with other transforms) :
void xmlXSLTParser(){
String xslFile = commonPath + "/xslt/inputPointCSVTOXML_style2.xsl";
String inputFile = "file:///" + commonPath + pointWorkFile;
String outputFile = commonPath + pointWorkFile + ".final";
try {
Processor proc = new Processor(false);
XsltCompiler comp = proc.newXsltCompiler();
XsltExecutable exp = comp.compile(new StreamSource(new File(xslFile)));
Serializer out = new Serializer();
out.setOutputProperty(Serializer.Property.METHOD, "xml");
out.setOutputProperty(Serializer.Property.INDENT, "yes");
out.setOutputFile(new File(outputFile));
XsltTransformer trans = exp.load();
trans.setInitialTemplate(new QName("main"));
//trans.setParameter(new QName("url-of-csv"),new XdmAtomicValue(inputFile));
trans.setDestination(out);
trans.transform();
System.out.println("Output written to text file");
} catch (SaxonApiException e) {
println("XSLT Error :" + e );
}
}
}
My Error in detail:
Error at char 6 in xsl:for-each-group/#select on line 10 column 59 of inputPointCSVTOXML_style2.xsl:
XPDY0002: The context item for axis step ./CLIENT is absent
XSLT Error :net.sf.saxon.s9api.SaxonApiException: The context item for axis step ./CLIENT is absent
Your Java code does not set any context item, instead it sets an initial template. So you will need to make sure you provide the input XML as the context item to the XsltTransformer, using http://saxonica.com/html/documentation/javadoc/net/sf/saxon/s9api/XsltTransformer.html#setInitialContextNode(net.sf.saxon.s9api.XdmNode) or as a Source, using http://saxonica.com/html/documentation/javadoc/net/sf/saxon/s9api/XsltTransformer.html#setSource(javax.xml.transform.Source).
So instead of trans.setInitialTemplate(new QName("main")); use trans.setSource(new StreamSource(inputFile));.

or condition not working as expected

I've the below XML statement
<section level="sect1">
<title><content-style font-style="bold">PRACTICE DIRECTIONS</content-style></title>
</section>
when i try to apply the below condition
<xsl:when test="not(contains(./title/content-style/text(),'PRACTICE DIRECTIONS'))">
<xsl:choose>
<xsl:when test="./section[1]/para/phrase">
<xsl:value-of select="./section[1]/para[1]/phrase"/>
<xsl:for-each select="./section">
<xsl:apply-templates select="." mode="toc"/>
</xsl:for-each>
</xsl:when>
</xsl:choose>
It is working fine
Here I've another condition.
<xsl:when test="not(contains(./title/content-style,'ORDER')) or not(contains(./title/content-style/text(),'PRACTICE DIRECTIONS')) or not(contains(./section[1]/title,'ORDER')) or not(contains(./section[1]/title,'PRACTICE DIRECTIONS'))">
Here this should not let the condition(or the cursor) to get into the inner choose condition as there is not(contains(./title/content-style/text(),'PRACTICE DIRECTIONS')) as one of the conditions.
But here in my case the cursor is moved into the inner choose. Where am I going wrong?
Use AND instead of OR and you'll probably get the result you're after.

Resources