I am using tinyxpath-1.3.1. C/C++ on Linux. When I do a xpath search on a document I am not finding nodes when I think I should.
My XML:
<data>
<event deviceId="25479545.5" interface="sensor-multilevel"
command="state" label="luminance" newValue="800"
oldValue="9" time="1412227484" />
</data>
My xpath Expression:
/data/event[#deviceId="25479545.5" and #interface="sensor-multilevel" and
#label="luminance" and #newValue>600 and #oldValue<10]
If I take the oldValue out and use () like this the comparison works:
/data/event[(#deviceId="25479545.5" and #interface="sensor-multilevel") and
(#label="luminance" and #newValue>600)]
Is there some limit on the number of comparisons?
Anything special with converting the 600 to a decimal?
Do I need to "" the value 600, it seems to work either way.
Any ideas on how to get it to work with the oldValue attribute included in the expression?
TinyXPath call:
TiXmlNode * node = TinyXPath::XNp_xpath_node( root, expression.c_str() );
Thanks
Larry
You seem to be getting a string comparison here rather than a numeric comparison ("9" > "10" in alphabetic order). That's incorrect according to the specs (both XPath 1.0 and 2.0, though they get there in rather different ways).
The safest approach is probably to convert explicitly to a number: write
number(#oldValue) < 10
Looks like the tinyxpath library has some bugs....
This works for tinyxpath-1.3.1:
/data/event[((#deviceId="25479545.5" and #interface="sensor-multilevel") and
(#label="luminance" and #newValue>"600")) and (#oldValue<"10")]
parenthesis were needed to group these in a final set of 2.
Or I just convert to using libxml2.
Related
The output below is expected. Given the xslt is sorted by SHIPPEDQTYPALLETNR
The Output xml below
<ORDER_DATA_Main>
<ORDER_DATA>
<SHIPPEDQTYPALLETNR>97915</SHIPPEDQTYPALLETNR>
<MASTER_P>0001</MASTER_P>
</ORDER_DATA>
<ORDER_DATA>
<SHIPPEDQTYPALLETNR>97916</SHIPPEDQTYPALLETNR>
<MASTER_P>0002</MASTER_P>
</ORDER_DATA>
<ORDER_DATA>
<SHIPPEDQTYPALLETNR>97917</SHIPPEDQTYPALLETNR>
<MASTER_P>0003</MASTER_P>
</ORDER_DATA>
</ORDER_DATA_Main>
xml file
<ORDER_DATA>
<ORDER_P>
<ORDER_QUANTITY>
<SKU_INFO>
<TQTY>260</TQTY>
</SKU_INFO>
<SHIPPED_GOODS_INFO_HEADER>
<SHIPPED_GOODS_INFO>
<CONTRYOFORIGIN>US</CONTRYOFORIGIN>
<SHIPPEDQTYPALLETNR>97916</SHIPPEDQTYPALLETNR>
</SHIPPED_GOODS_INFO>
<MASTER_PALETTE>
<MASTER_P>0002</MASTER_P>
</MASTER_PALETTE>
</SHIPPED_GOODS_INFO_HEADER>
</ORDER_QUANTITY>
</ORDER_P>
<ORDER_P>
<ORDER_QUANTITY>
<SKU_INFO>
<TQTY>250</TQTY>
</SKU_INFO>
<SHIPPED_GOODS_INFO_HEADER>
<SHIPPED_GOODS_INFO>
<CONTRYOFORIGIN>US</CONTRYOFORIGIN>
<SHIPPEDQTYPALLETNR>97915</SHIPPEDQTYPALLETNR>
</SHIPPED_GOODS_INFO>
<MASTER_PALETTE>
<MASTER_P>0001</MASTER_P>
</MASTER_PALETTE>
</SHIPPED_GOODS_INFO_HEADER>
</ORDER_QUANTITY>
</ORDER_P>
</ORDER_DATA>
below xslt snippet
xsltsnippet
xsltcode
<MASTER_P>
<xsl:value-of select="//MASTER_PALETTE[//SHIPPED_GOODS_INFO/SHIPPEDQTYPALLETNR=$v_pallett]/MASTER_P"/>
</MASTER_P>
MASTER_PALETTE and SHIPPED_GOODS_INFO are siblings. When you write
select="//MASTER_PALETTE[//SHIPPED_GOODS_INFO...]
the // within the predicate means the predicate will be true if any SHIPPED_GOODS_INFO anywhere in the document satisfies the conditions, regardless whether it is related in any way to the MASTER_PALETTE being tested. Instead of // here, try ../.
I have the following xml doc:
<GetGeneralServiceRequestByReferenceValueResponse xmlns="http://www.caps-solutions.co.uk/webservices/connectors/731/servicerequest/messagetypes">
<GeneralServiceRequest xmlns="http://www.caps-solutions.co.uk/schema/uniform/731/servicerequest/sr/srtypes">
<ServiceRequestIdentification>
<ServiceRequestTechnicalKey>PG7ECIJBKFX00</ServiceRequestTechnicalKey>
<ReferenceValue>18/009969/S_SCBC</ReferenceValue>
<AlternativeReferences>
<AlternativeReference xmlns="http://www.caps-solutions.co.uk/schema/uniform/72b/common/uniformtypes">
<ReferenceValue>W44811182451</ReferenceValue>
<ReferenceType>UTRN</ReferenceType>
</AlternativeReference>
<AlternativeReference xmlns="http://www.caps-solutions.co.uk/schema/uniform/72b/common/uniformtypes">
<ReferenceValue>00482</ReferenceValue>
<ReferenceType>BAD</ReferenceType>
</AlternativeReference>
</AlternativeReferences>
<SiteID>JB</SiteID>
</ServiceRequestIdentification>
</GeneralServiceRequest>
</GetGeneralServiceRequestByReferenceValueResponse>
I need to select the <ReferenceValue> that has a sibling <ReferenceType> of "UTRN"
The following xpath get's me the <ReferenceValue> of the last <Alternative> reference.
/*[local-name()='GetGeneralServiceRequestByReferenceValueResponse']/*[local-name()='GeneralServiceRequest']/*[local-name()='ServiceRequestIdentification']/*[local-name()='AlternativeReferences']/*[local-name()='AlternativeReference']/*[local-name()='ReferenceValue']
I've tried using [] for the parent AlternativeReference node then ReferenceValue='UTRN' but haven't been able to get the required output.
Don't use local-name(). Declare the namespaces in your XSLT and use the prefixes.
For example, declare them like this (you can pick any prefixes you like, as long as the namespace URIs match):
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:msg="http://www.caps-solutions.co.uk/webservices/connectors/731/servicerequest/messagetypes"
xmlns:sr="http://www.caps-solutions.co.uk/schema/uniform/731/servicerequest/sr/srtypes"
xmlns:uni="http://www.caps-solutions.co.uk/schema/uniform/72b/common/uniformtypes"
exclude-result-prefixes="msg sr uni"
>
And use (wrapped for legibility, but XPath is not whitespace-sensitive, you can wrap it the same way in your XSLT):
/msg:GetGeneralServiceRequestByReferenceValueResponse
/sr:GeneralServiceRequest
/sr:ServiceRequestIdentification
/sr:AlternativeReferences
/uni:AlternativeReference[uni:ReferenceType = 'UTRN']
/uni:ReferenceValue
Here, /uni:AlternativeReference[uni:ReferenceType = 'UTRN'] only selects those <AlternativeReference> nodes who have a <ReferenceType> child of the wanted value.
Since there are many ways to look at an XML tree (and because I think that explicitly. naming. every. single. step. along. the. way. is overdoing it), something like this would also work:
//uni:ReferenceType[. = 'UTRN']/../uni:ReferenceValue
or
//uni:AlternativeReference[uni:ReferenceType = 'UTRN']/uni:ReferenceValue
You can add criteria to the <AlternativeReference> predicate to restrict the selection of <AlternativeReference> elements to those that have a <ReferenceType> element who's value is "UTRN":
/*[local-name()='GetGeneralServiceRequestByReferenceValueResponse']/
*[local-name()='GeneralServiceRequest']/
*[local-name()='ServiceRequestIdentification']/
*[local-name()='AlternativeReferences']/
*[local-name()='AlternativeReference' and *[local-name()='ReferenceType' and .='UTRN']]/
*[local-name()='ReferenceValue']
This should do:
"//AlternativeReference[1]/ReferenceValue"
Or if you know the sibling value:
"//ReferenceType[text()='UTRN']/parent::*/ReferenceValue"
I am trying to change all instances of a number in an xml file. The constant 45 should be added to the number.
Temp is the following text:
<rownum value="1">
<backupapplication>HP Data Protector</backupapplication>
<policy>AUTDR12_Daily</policy>
<policytype>FileSystem</policytype>
<dataretained>31</dataretained>
<fullbackup>7</fullbackup>
<backuptime>0.17</backuptime>
<retentionperiod>Short</retentionperiod>
<peakmbps>11</peakmbps>
<backupcategory>Fulls & Fulls</backupcategory>
</rownum>
<rownum value="2">
<backupapplication>HP Data Protector</backupapplication>
<policy>AUTP_Appl_Monthly</policy>
<policytype>FileSystem</policytype>
<dataretained>268</dataretained>
<fullbackup>91</fullbackup>
<backuptime>2.31</backuptime>
<retentionperiod>Long</retentionperiod>
<peakmbps>12</peakmbps>
<backupcategory>Fulls & Fulls</backupcategory>
</rownum>
I tried the following code:
NeedleRegEx = <rownum value="(\d+)">
Replacement = <rownum value="($1+45)">
Temp := RegExReplace(Temp, NeedleRegEx, Replacement)
But this changes it into
<rownum value="1+45">
while I want
<rownum value="46">
How do I do this in AutoHotKey?
RegEx aren't designed to evaluate mathematical expressions. There are some languages, in which you can use a replacing function that can do dynamic replacements (e.g. JavaScript). But no such luck in AHK.
Using RegEx for the purpose of parsing XML documents isn't good practice anyway. I suggest using an XML parser instead. For AHK, you can utilize a COM object of MSXML2.DOMDocument. Here's an example (and further references) of how to use it: http://www.autohotkey.com/board/topic/56987-com-object-reference-autohotkey-v11/page-2#entry367838.
What you want to do is parse your XML to a DOM document and loop over every rownum tag. Now, you can retrieve the value attribute, increment it, and overwrite the attribute with the new value.
Update
To the code you've posted in the comments: There were some minor mistakes and one big mistake. The big mistake was trying to parse non-valid XML. You can check your XML files by feeding them to a formatter/validator. The loadXml()method will return false if there was a parsing error. The method obj.saveXML() does not exist. If you want to retrieve the document's string representation, simply access its xml property: obj.xml. If you want to save it to a file, there's the built-in method save(filepath).
Here's my suggestion for a clean approach (yes, you CAN use meaningful variable names!):
doc := ComObjCreate("MSXML2.DOMDocument.6.0")
if(!doc.loadXml(xmlString)) {
msgbox % "Hey! That's no valid XML!"
ExitApp
}
rownums := doc.getElementsByTagName("rownum")
Loop % rownums.length
{
rownum := rownums.item(A_Index-1)
value := rownum.getAttribute("value")
value += 45
rownum.setAttribute("value", value)
}
doc.save("myNewFile.xml")
I have the following selector, which works:
parent::node()/myNS:expField[myNS:Nam='NAMETOFIND']/myNS:Val
What I want is to do case-insensitive matching on the myNS:Nam value, so I would be able to select <Val> from any of the following:
<expField>
<Nam>NAMETOFIND</Nam>
<Val>the value I want</Val>
</expField>
<expField>
<Nam>NameToFind</Nam>
<Val>the value I want</Val>
</expField>
<expField>
<Nam>nametofind</Nam>
<Val>the value I want</Val>
</expField>
<expField>
<Nam>nAmEtOFInD</Nam>
<Val>the value I want</Val>
</expField>
I'm using XSLT 1, so I can't use lower-case().
translate() will do the job, it's not pretty but it works. If you know what language you want to process, that is.
I am dealing with some auto-generated XSLT code.
It contains the following:
string(string(.))
number(string(.))
string(number(string(.)))
Is there any point to these? Or are they reducible to
string(.)
number(.)
string(.)
?
Like Martin says.
There are edge cases in XPath 2.0 where number(string(.)) is not exactly the same as number(.), for example if the context item is an instance of xs:gYear then number(.) will fail but number(string(.)) will succeed; contrariwise, if the context item is a boolean, number(.) will convert true to 1 and false to 0, while number(string(.)) converts both to NaN. But it's very unlikely that these edge cases are important to your application.
For the first one I am pretty sure it can be reduced to string(.). For the third one I don't think you can reduce it to string(.) as for instance for the context node having a character as its string content (e.g. <foo>a</foo>) doing number(string(.)) gives you the special number value "not a number" and if you do string() on that again you get (http://www.w3.org/TR/xpath/#section-Number-Functions, http://www.w3.org/TR/xpath/#section-String-Functions) the string "NaN". I am not sure about the second being reducible, maybe you can check the details of edge cases with the links I provided.