I want to count no of elements under a node , In below XML I has 2 report entries , In first entry I has 4 elements and in 2nd entry I has only 3 address is missing.
I want to count the total elements under each report_entry
count is 4 for first report entry and count is 3 for 2nd report entry
Can some body suggest is it possible ? any sample code ?
example xml:
<?xml version="1.0" encoding="UTF-8"?>
<report_data>
<report_entry>
<id>12345</id>
<fname>Venkata</fname>
<lname>Penumatsa</lname>
<address>Hyderabad</address>
</report_entry>
<report_entry>
<id>123453</id>
<fname>stephen</fname>
<lname>florida</lname>
</report_entry>
</report_data>
-- Venkata
Use
<xsl:template match="report_entry">
<xsl:value-of select="count(*)"/>
</xsl:template>
if you "want to count the total elements under each report_entry".
Related
I am looking to find a way to control both start and end numbering that are added as suffix number to text "period". It seems the logic of my code makes for-each count 3 search matches (index 1-3), and due to using if to find se:Bank it reduces printout to 2 prints.
I know I can use position() -2 to force the numbering to start at 0, but that will only work if I have exact same amount of data. As soon as data growths the position() -2 will assume to step 2 positions minus and will not return zero as start.
I do understand that XSLT does what I actually ask for and returns presumable correct answer.
The interval of number I will be using is between 0 and 3. Most of the time in sequence.
It will be unknown in advance exact interval amounts.
I know there is a start-at using <xsl:number> but it did not solve my problem. Using start-at prints out same number twice.
The JSON file is aligned with a certain standard so I am not allowed to change the structure of the JSON file.
Suspected problem:
The "foreach" spans over more loops than wanted. In this case I need just for the system to loop twice for "se:Bank" and therefor return "period0" and "period1".
Observation:
I suspect that it probably would be better that I learn to extract when xbrl:concept = se:Bank. That would reduce the planned loop down to 2 search, thus being able iterate over them.
Here you find the xsltfiddle.
Below you find same code as in xsltfiddle:
JSON data:
<data>
{
"report": {
"facts": [
{
"xbrl:concept": "se:CompanyName",
"value": "Great Company Ltd"
},
{
"xbrl:concept": "se:Bank",
"numericValue": 1000
},
{
"xbrl:concept": "se:Bank",
"numericValue": 3000
}
]
}
}
</data>
XSL:
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0">
<xsl:output method="xhtml" indent="yes" html-version="5"/>
<xsl:mode on-no-match="shallow-skip"/>
<!-- Parse JSON to XML -->
<xsl:template match="data">
<xsl:apply-templates select="json-to-xml(.)/*"/>
</xsl:template>
<!-- Printout periods -->
<xsl:template match="//*[#key='facts']">
<xsl:for-each select="//*[#key='xbrl:concept']">
<xsl:if test=". = 'se:Bank'">
period<xsl:number value="position()"/>
</xsl:if>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
Result:
<?xml version="1.0" encoding="UTF-8"?>period2period3
Expected result:
<?xml version="1.0" encoding="UTF-8"?>period0period1
If you want to output position() or position() - 1 then <xsl:value-of select="position()"/> or <xsl:value-of select="position() -1 "/>, respectively, suffice, there is no need to feed position() to xsl:number.
Furthermore, I am not sure I understand your requirements correctly, but using a predicate <xsl:for-each select=".//*[#key='xbrl:concept'][. = 'se:Bank']"> instead of the of the nested xsl:if should help to get the result you want, namely to process the two elements in the input sample meeting the condition in the predicate: https://xsltfiddle.liberty-development.net/93wniUS/1
I am using xsl 1.0.
I have this xml I need to strip elements out of if they have certain children or siblings:
<?xml version="1.0" encoding="UTF-8"?>
<SyncDMMAXINTOBJECT xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
creationDateTime="2019-09-08T15:53:31-04:00" transLanguage="EN" baseLanguage="EN"
messageID="1" maximoVersion="7 6 20181212-1733 V76010">
<DMMAXINTOBJECTSet>
<MAXINTOBJECT action="AddChange">
<ALIASCONFLICT>0</ALIASCONFLICT>
<MAXINTOBJDETAIL>
<ALTKEY></ALTKEY>
<OBJECTNAME>WORKORDER</OBJECTNAME>
<MAXINTOBJCOLS>
<NAME>ACTCONSTCOST</NAME>
<OBJECTNAME>WORKORDER</OBJECTNAME>
</MAXINTOBJCOLS>
<MAXINTOBJCOLS>
<NAME>ACTCONSTCOST2</NAME>
<OBJECTNAME>WORKORDER</OBJECTNAME>
</MAXINTOBJCOLS>
<MAXINTOBJALIAS>
<ALIASNAME>CODEPARK</ALIASNAME>
<NAME>CODEPARK</NAME>
</MAXINTOBJALIAS>
<MAXINTOBJALIAS>
<ALIASNAME>CODEPARK2</ALIASNAME>
<NAME>CODEPARK2</NAME>
</MAXINTOBJALIAS>
</MAXINTOBJDETAIL>
</MAXINTOBJECT>
</DMMAXINTOBJECTSet>
</SyncDMMAXINTOBJECT>
I would like to setup a template to match all elements of the type MAXINTOBJCOLS
if they have a child element NAME with the value of ACTCONSTCOST and a child element OBJECTNAME with the value of WORKORDER, or if they have a parent element MAXINTOBJDETAIL with a child element OBJECTNAME with the value of WORKORDER, and a child element ALIASNAME with the value of CODEPARK.
So If I ran my template and it was empty it would turn the above XML into this (2 elements have been moved):
<?xml version="1.0" encoding="UTF-8"?>
<SyncDMMAXINTOBJECT xmlns="http://www.ibm.com/maximo" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
creationDateTime="2019-09-08T15:53:31-04:00" transLanguage="EN" baseLanguage="EN"
messageID="1" maximoVersion="7 6 20181212-1733 V76010">
<DMMAXINTOBJECTSet>
<MAXINTOBJECT action="AddChange">
<ALIASCONFLICT>0</ALIASCONFLICT>
<MAXINTOBJDETAIL>
<ALTKEY></ALTKEY>
<OBJECTNAME>WORKORDER</OBJECTNAME>
<MAXINTOBJCOLS>
<NAME>ACTCONSTCOST2</NAME>
<OBJECTNAME>WORKORDER</OBJECTNAME>
</MAXINTOBJCOLS>
<MAXINTOBJALIAS>
<ALIASNAME>CODEPARK2</ALIASNAME>
<NAME>CODEPARK2</NAME>
</MAXINTOBJALIAS>
</MAXINTOBJDETAIL>
</MAXINTOBJECT>
</DMMAXINTOBJECTSet>
</SyncDMMAXINTOBJECT>
I am using this XSL to duplicate but I can't seem to figure out the match given the parent child element relationships -
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:max="http://www.ibm.com/maximo" exclude-result-prefixes="max">
<xsl:output omit-xml-declaration="yes"/>
<xsl:template match="node()|#*">
<xsl:copy>
<xsl:apply-templates select="node()|#*"/>
</xsl:copy>
</xsl:template>
<xsl:template match="Element[#attr1='one' and #attr2='two']"/>
</xsl:stylesheet>
I believe I have to replace this one with 2 others to achieve my goal:
(does nothing but proof of concept - the second one clears the changeby attribute)
<xsl:template match="Element[#attr1='one' and #attr2='two']"/>
<xsl:template match="max:SyncDMMAXINTOBJECT/max:DMMAXINTOBJECTSet/max:MAXINTOBJECT/max:CHANGEBY/text()"/>
You can use the following empty template in combination with the identity template:
<xsl:template match="max:MAXINTOBJCOLS[(max:NAME='ACTCONSTCOST' and max:OBJECTNAME='WORKORDER3') or (../../max:MAXINTOBJDETAIL[max:OBJECTNAME='WORKORDER' and .//max:ALIASNAME='CODEPARK'])]"/>
It matches both max:MAXINTOBJCOLS elements and removes them. The rest is copied. The .//max:ALIASNAME checks if max:MAXINTOBJDETAIL has a descendant named max:ALIASNAME. It does not check only for a direct child.
I have a fairly nested XML file that I'd like to transform with an XSL template to something a little simpler to make bulk loading the data into SQL more efficient. I wanted to do it in C++ (Codeblocks with gcc) but I'm having a bit of trouble just being able to load the document with any of the libraries I've come across, including MSXML. If anyone has any experience using MSXML in Codeblocks with gcc let me know!
I have a stylesheet that transforms the XML in Excel VBA with a DOMDocument but I don't want to depend on Excel. I figured the next best thing would be a VBScript.
The data are one or two text values that are held in <DATAVALUE> nodes, descendants of 100 <LOCATION> nodes. The first child of each <LOCATION> node, called <LOCATIONNAME>, holds a unique name for each <LOCATION> node (i.e; NAME1-NAME100). The third and fourth children of the <LOCATION> node (if there is a fourth child) are <DATA> nodes, each holding a <DATAVALUE> node. The file can have upwards of 1 million <SAMPLE> nodes. Here is the XML:
<?xml version="1.0" encoding="utf-8"?>
<MYImportFile xmlns="urn:ohLookHEREaNamespacedeclaration">
<HEADERVERSION>1.10</HEADERVERSION>
<MESSAGE>Import</MESSAGE>
<MYBED>QUEEN</MYBED>
<SOURCE>SPRING </SOURCE>
<USERID>MMOUSE</USERID>
<DATETIME>2019-11-25T12:31:00</DATETIME>
<SAMPLE TYPE="No" APPLE="false">
<SAMPLEID>0000565</SAMPLEID>
<SAMPLECATEGORY>CLASS5</SAMPLECATEGORY>
<LOCATION APPLE="false">
<LOCATIONNAME>NAME1</LOCATIONNAME>
<READBY>MMOUSE</READBY>
<TIME>12:31:00</TIME>
<DATA>
<DATAVALUE>aaaa</DATAVALUE>
</DATA>
<DATA>
<DATAVALUE>bbbb</DATAVALUE>
</DATA>
</LOCATION>
'''''''''''''''''there are 100 LOCATION entries''''''''''''''''''''''''
<LOCATION APPLE="false">
<LOCATIONNAME>NAME100</LOCATIONNAME>
<READBY>MMOUSE</READBY>
<TIME>12:31:00</TIME>
<DATA>
<DATAVALUE>zzzz</DATAVALUE>
</DATA>
</LOCATION>
</SAMPLE>
'''''''''''''''''repeat for however many SAMPLES there are''''''''''''''''''''''
</MYImportFile>
I want to point something out so it's a little more clear what's going on. In the transformed xml document, one of the things I need to account for is when there is only one <DATA> node in a <LOCATION>. This is done by copying the first <DATAVALUE> node into a second <DATAVALUE> node in the new document. For example, the <DATAVALUE>, "zzzz" that appears twice in the transformed sheet only appears in the initial XML once. Here is what I want the transformed XML to look like:
<?xml version="1.0" encoding="UTF-8"?>
<MYImportFile>
<SAMPLE>
<SAMPLEID>0000565</SAMPLEID>
<NAME1_1>aaaa</NAME1_1>
<NAME1_2>bbbb</NAME1_2>
<NAME2_1>cccc</NAME2_1>
<NAME2_2>dddd</NAME2_2>
'''''''''''''''''there are 100 LOCATION entries transformed to NAME1-NAME100''''''''''''''''''''''''
<NAME100_1>zzzz</NAME100_1>
<NAME100_2>zzzz</NAME100_2>
</SAMPLE>
'''''''''''''''''repeat for however many SAMPLES there are''''''''''''''''''''''
</MYImportFile>
My StyleSheet (that works with VBA code):
<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:b="urn:ohLookHEREaNamespacedeclaration" exclude-result-prefixes="b">
<xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>
<xsl:template match="/b:MYImportFile">
<MYImportFile>
<xsl:for-each select="b:SAMPLE">
<SAMPLE>
<SAMPLEID>
<xsl:value-of select="b:SAMPLEID"/>
</SAMPLEID>
<NAME1_1>
<xsl:value-of select="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[1]/b:DATAVALUE"/>
</NAME1_1>
<xsl:choose>
<xsl:when test="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[2]/b:DATAVALUE">
<NAME1_2>
<xsl:value-of select="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[2]/b:DATAVALUE"/>
</NAME1_2>
</xsl:when>
<xsl:otherwise>
<NAME1_2>
<xsl:value-of select="b:LOCATION/b:LOCATIONNAME[text() = 'NAME1']/../b:DATA[1]/b:DATAVALUE"/>
</NAME1_2>
</xsl:otherwise>
</xsl:choose>
'''''''''''''''''''there are 100 NAME entires to recieve the 100 locations
</SAMPLE>
</xsl:for-each>
</MYImportFile>
</xsl:template>
</xsl:stylesheet>
My Script:
Option Explicit
Const strInputFile = "C:\Path\fileName.xml"
Const strTemplateFile = "C:\Path\convFileName.xsl"
Const strOutputFile = "C:\Path\newFileName.xml"
Dim objXMLDoc : Set objXMLDoc = WScript.CreateObject("Msxml2.DOMDocument")
objXMLDoc.async = False
objXMLDoc.loadXML(strInputFile)
objXMLDoc.SetProperty "SelectionNamespaces", "xmlns='urn:myNamespace'"
Dim objXSLDoc : Set objXSLDoc = WScript.CreateObject("Msxml2.DOMDocument")
objXSLDoc.async = False
objXSLDoc.loadXML(strTemplateFile)
Dim objNewXMLDoc : Set objNewXMLDoc = WScript.CreateObject("Msxml2.DOMDocument")
objXMLDoc.transformNodeToObject objXSLDoc, objNewXMLDoc
objNewXMLDoc.save strOutputFile
The error:
Line: 19
Char: 1
Error: The stylesheet does not contain a document element. The
stylesheet may be empty, or it may not be a well-formed XML document.
Code: 80004005
Source: msxml3.dll
I'm guessing either my script isn't quite right or there's a setting I'm missing, causing mismatching objects and libraries, because my VBA macro transforms the xml with that stylesheet. Anyone have any ideas? Suggestions to make this thing run?
As far as I remember loadXML takes a string with the XML. If you have a file or URL to parse use the load method.
Below is my source schema
<ns0:xyz xmlns:ns0="http://abc/xyz">
<main>
<zzz/></zzz>
<yyy/></yyy>
</main>
<Lines>
<Line>
<LineNum></LineNum>
<Linerate></Linerate>
</Line>
</Lines>
and Below is my input file:
<?xml version="1.0" encoding="ISO-8859-1"?>
<xyz xmlns="http://abc/xyz">
<main>
<zzz>12</zzz>
<yyy>11</yyy>
</main>
<Lines>
<Line>
<LineNum>1</LineNum>
<Linerate>0.5</Linerate>
</Line>
<LineNum>3</LineNum>
<Linerate>0.2</Linerate>
</Line>
<Line>
<LineNum>5</LineNum>
<Linerate>0.5</Linerate>
</Line>
</Lines>
I need to compare the line records and check if the Linerate element has similar value , then irrespective of the number of records in the source file , my destination file should have only 1 record with that particular Line rate value.
And for every distinct Linerate value , there should be respective record for it.
The Linerate value should be assigned to percent node in the destination
The Cumulative sum value of the LineNum value should be assigned to Amount in destination(if Linerate value is same across the records in the source)
Linerate * cumulative sum of LineNum should be assinged to AdditionalAmount in destination
Below is the expected output file
<LineSummary>
<Percent>0.5</Percent>
<Amount>6</Amount>
<AdditionalAmount>3</AdditionalAmount>
</LineSummary>
<LineSummary>
<Percent>0.2</Percent>
<Amount>3</Amount>
<AdditionalAmount>0.6</AdditionalAmount>
</LineSummary>
Below is the XSLT code used in my BizTalk Map.
<xsl:variable name="unique-LineRate" select="//Lines/Line[not(Linerate=preceding-sibling::Line/Linerate)]/Linerate" />
<xsl:for-each select="$unique-LineRate">
<LineSummary>
<xsl:variable name="LineSum" select="sum(//Lines/Line[Linerate=current()]/LineNum)" />
<Percent><xsl:value-of select="current()"/></Percent>
<Amount><xsl:value-of select="$LineSum" /></Amount>
<AdditionalAmount><xsl:value-of select="current() * $LineSum"/></AdditionalAmount>
</LineSummary>
</xsl:for-each>
If my source schema "ElementFormDefault" value is kept as unqualified or default, the I get an error as : Input validation error: The element 'xyz' in namespace 'http://abc/xyz' has invalid child element 'main' in namespace 'http://abc/xyz'. List of possible elements expected: 'main'.
If my source schema "ElementFormDefault" is kept as Qualified,then the XSLT doesn't work. I am sure it has to do something with the namespace issue or element tagging But i am not sure exactly where i need to make the change.
Do I need to prefix with the XPath or the namespace to all the elements in the XSLT?
I need to count the number of students who have failed an exam or those with some wrong data. This if loop condition is working properly, but the count of the number of students is wrong.
<xsl:if test ="$StudentFinalmark > 100 or $StudentFinalmark < 49">
<xsl:value-of select = "count(StudentFinalmark)"/>
</xsl:if>
For example, if there are four failed students and students with two wrong dates, it's coming as 111111
Please help me with the solution.
Assuming that your xml looked like this (as per your comment below):
<Students>
<StudentResults>
<Marks>
<StudentAssign1>40</StudentAssign1>
<StudentAssign2>40</StudentAssign2>
<StudentExam>40</StudentExam>
</Marks>
</StudentResults>
<StudentResults>
<Marks>
<StudentAssign1>1</StudentAssign1>
<StudentAssign2>2</StudentAssign2>
<StudentExam>3</StudentExam>
</Marks>
</StudentResults>
<StudentResults>
<Marks>
<StudentAssign1>33</StudentAssign1>
<StudentAssign2>33</StudentAssign2>
<StudentExam>33</StudentExam>
</Marks>
</StudentResults>
</Students>
an XSLT like this will return the results you are after:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="/">
<xsl:element name="Output">
<xsl:value-of select="count(Students/StudentResults[sum(Marks/*) > 100 or sum(Marks/*) < 49])" />
</xsl:element>
</xsl:template>
</xsl:stylesheet>
This returns the result:
<Output>2</Output>
It returns two because, in the example XML above, the first StudentResults has a sum of 120 (which fails because it is bigger than 100) and the second has a sum of 6 (which fails because it is less than 49)
The trick is to output a single item which is a count of the elements which you select via xpath, not via an "if" and "for" combination. There isn't a need to use a variable either.