PxPlus User Forum
Main Board => Discussions => Programming => Topic started by: Josh Fake on March 29, 2019, 10:19:05 AM
-
To All:
I am in the process of moving away from the MS XML Parser MSXML2.DomDocument and replacing with the native XML object parser *obj/XML. 95% of the code is easily interpreted from using the MS COM Object to the Pvx Parser, but this last issue.
I have an incoming XML response from a webservice (this is just a "small snippet") with a list of a very large of Question Subnodes within the Question Node.
<Questions Level="Product">
<Question Name="SpecialtyShape">
<Answer>None</Answer>
</Question>
<Question Name="MV_ConfigIDListVisual">
<Answer>1340</Answer>
<Answer>1348</Answer>
</Question>
<Question Name="PricingGroup">
<Answer>L06</Answer>
</Question>
<Question Name="Shape">
<Answer>Rectangular</Answer>
</Question>
<Question Name="InsectScreen">
<Answer>No</Answer>
</Question>
<Question Name="ConfigIDAccessoryGrid">
<Answer>-29</Answer>
</Question>
<Question Name="NetPriceExtended">
<Answer>2951.10</Answer>
</Question>
<Question Name="InsectScreenMaterial">
<Answer>N/A</Answer>
</Question>
</Questions>
Within the MSXML DOM Document I could easily find a Question based on the Name attributes value.
shape_node=xml'selectsinglenode("Questions/Question[@Name=""Shape""]")
shape$=shape_node'getelementsbytagname(""Answer"")'item(0)'text$
Is there any method within the PxPlus XML object to find a node based on an attributes' value as it can be achieved in the MS XML Object?
I could loop through all questions (almost 200) and create a memory file using the Name of the Question as the primary key, but that would involve more changes than I was hoping to make, I justed was hoping to replace the object so the code was not limited to a windows o/s.
Thanks for any and all help ahead of time
-
You can use the xml() function to loop through the xml nodes, and the attribute nodes as well.
open (unt,isz=1)"test.xml"
read record (lfo,siz=9999999)xml$
close (lfo)
let ofs=0
let xml1$=xml(next from xml$,ind=ofs,key=tag$) ! get first level xml
let ofs1=0
while 1
let xml2$=xml(next from xml1$,ind=ofs1,key=tag$,opt=prop$,err=*break) ! loop through tags
let propofs=0
while 1
let propval$=xml(property next from prop$,ind=propofs,key=propname$,err=*break) ! loop through attributes until Name="Shape"
if propname$="Name" and propval$="Shape" then let found=1; break
wend
if found then break
wend
print xml2$
-
Thank you Allen!
I will try this as I have never worked directly with the xml() function and see if this will work. I have about 30 of these I have to navigate through so I will probably have to add a switch/case/end switch while transversing through all the question sub-nodes. But I will definitely try this.
I was just hoping there would be a way to handle this via the xml class find_node() method. Most of the methods I use in the MS Com Object are easily converted to a method within the*obj/XML interface with the exception of this one. I'll add this as an "enhancement request".
-
We have a similar attribute search functionality in the UnForm xmlreader object, which internally is using the pxplus xml() function. If the customer has UnForm, like a lot of yours do, you could write a simple job that uses that object.
-
Thanks Allen, this client uses Unform extensively. I might check it out as I could use your REST interfaces to post the Question Node and have it return a response back to FACTS as a new alternative.
-
Actually the easiest solution might be to use the *obj/xml object we supply.
You can load it with the contents of the XML file then use Find_Node to find desired node/attribute.
Here is the code based on the XML you provided having been put in the file test.xml:
0010 LET oXml=NEW("*obj/xml" FOR PROGRAM) ! The FOR PROGRAM drops when done
0020 OPEN (1,ISZ=-1)"test.xml"
0030 READ RECORD (1,SIZ=1000000)R$ ! Read whole file into string
0040 CLOSE (1)
0050 oXml'set_xml(R$)
0060 LET oNode=0
0070 LET oQuestions=oXml'Find_node("Questions")
0080 WHILE 1
0090 LET oNode=oQuestions'Find_node("Question",oNode)
0100 IF oNode=0 THEN BREAK
0110 IF oNode'Find_Attr("Name")'Value$="Shape" THEN BREAK
0120 WEND
0130 IF oNode=0 THEN PRINT "not found" ELSE PRINT oNode'Find_node("Answer")'Value$
The steps were:
- Create the XML object
- Read the whole file into memory (used a dummy large SIZ= to assure whole file)
- Passed file to Set_xml method
- Found the Questions Node
- Loop through each Question looking for any question with attribute of "Name" = "Shape"
- Printed our the value of the 'Answer" node for that Question.
-
Josh,
Since PVX has object inheritance, I would suggest a re-class of the xml object to add Mikes attribute search function so the code is reusable and not in the edited programs. If the process does a large amount of searching in large files, adding an override of the *obj/xml parser routine to add a memory file with alternate keys on the attributes etc would be warranted.