Android Question Sort in the XPath query for XML file XOM lib

Valeriy Lakhtin

Member
Licensed User
Please, help me find an example of how to perform sorting in XPath a query when i work with XML file. I found a description example XPath query with SORT for Java in intrnet, but it does not work in B4A.
Example XML file
B4X:
<?xml version="1.0"?>
<dataroot>
<comp nam="QQQQ">
<rating>12</rating>
</comp>
<comp nam="eeee">
<rating>8</rating>
</comp>
<comp nam="ssss">
<rating>21</rating>
</comp>
</dataroot>
I need to get a list of "comp" sorted by "rating". Example code not work in B4A
B4X:
strQuery="//comp order by /rating"
xomCompanys=xomRootDoc.Query(strQuery)
 
Last edited:

sorex

Expert
Licensed User
Longtime User
no need for extra parsing libs...

try this

B4X:
Dim xml As String=File.ReadString(File.DirAssets,"xml.xml").Replace(Chr(13),"").Replace(Chr(10),"")
Dim m As Matcher
Dim l As List
Dim t As String
l.Initialize
m=Regex.Matcher("<comp nam=\""(.*?)\""><rating>(.*?)</rating>",xml)
Do While m.Find
t="000000"& m.Group(2)
l.Add( t.SubString(t.Length-6) & "-" & m.Group(1) )
Loop
l.Sort(True)
Log(l)

output > (ArrayList) [000008-eeee, 000012-QQQQ, 000021-ssss]
 
Upvote 0

sorex

Expert
Licensed User
Longtime User
Generally speaking, it is a mistake to parse XML with regular expressions.

not if you do it right and know what to grab :)

if they change a keyname name your parser will fail aswell so that risk is always there.
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
There are many valid modifications that can be done to the XML which will cause the regex parsing to fail while a proper parser will not be affected (a new attribute for example).

This is how I would have implemented it:
B4X:
Sub Process_Globals
   Type Comp (Name As String, Rating As Int)
   Private Comps As List
   Private parser As SaxParser
End Sub

Sub Globals

End Sub

Sub Activity_Create(FirstTime As Boolean)
   If FirstTime Then
     Comps.Initialize
     parser.Initialize
     Dim in As InputStream = File.OpenInput(File.DirAssets, "1.txt")
     parser.Parse(in, "parser")
     in.Close
     Comps.SortType("Rating", True)
     Log(Comps)
   End If
End Sub

Sub Parser_StartElement (Uri As String, Name As String, Attributes As Attributes)
   If Name = "comp" Then
     Dim c As Comp
     c.Initialize
     c.Name = Attributes.GetValue2("", "nam")
     Comps.Add(c)
   End If
End Sub

Sub Parser_EndElement (Uri As String, Name As String, Text As StringBuilder)
   If Name = "rating" Then
     Dim c As Comp = Comps.Get(Comps.Size - 1)
     c.Rating = Text.ToString
   End If
End Sub
 
Upvote 0

Valeriy Lakhtin

Member
Licensed User
Thanks Erel, for the tip, I know that you like SAX technology. Now for me is important to work with XML elements entirely, without sharing them on parts. DOM allows make to sort the internal element without destroying the structure all XML document. This is useful to save the document after processing. Your SAX technology can parse(sort) the individual rows in element. If we want to save document by SAX we must to handle all strings of the document again. according to the new members will have to re-parse the entire document. For me, this option seems to be more difficult. So I like DOM technology. Sorry for my bad english.

Here is an example of code in which I point to the element, changing its content and then save the whole document. It is checked and working
B4X:
            DateTime.DateFormat="dd/MM/yyyy HH:mm:ss"
            dblDT=DateTime.Now
            strTime=DateTime.Date(dblDT)

'            strQuery="//group[@g_name='" & strGrup & "']"
'            xomGroups=xomRootNode.Query(strQuery)
'            xomGroup=xomGroups.GetNode(0)
            Cal=xomGroup.GetFirstChildElementByName("loc_rating").value
            dblCal=Cal+1
            Cal=dblCal
            xomGroup.GetFirstChildElementByName("loc_rating").RemoveChildren
            xomGroup.GetFirstChildElementByName("loc_rating").AppendChild(Cal)
            xomGroup.GetFirstChildElementByName("time").RemoveChildren
            xomGroup.GetFirstChildElementByName("time").AppendChild(strTime)
         
'            strQuery="//object[@ID='" & strID & "']"
'            xomCompanys=xomRootNode.Query(strQuery)
'            xomCompany=xomCompanys.GetNode(0)
            Cal=xomCompany.GetFirstChildElementByName("loc_rating").value
            dblCal=Cal+1
            Cal=dblCal
            xomCompany.GetFirstChildElementByName("loc_rating").RemoveChildren
            xomCompany.GetFirstChildElementByName("loc_rating").AppendChild(Cal)
            xomCompany.GetFirstChildElementByName("time").RemoveChildren
            xomCompany.GetFirstChildElementByName("time").AppendChild(strTime)

'            strQuery="//object[@ID='" & strID & "']/phone[@num='" & strPhone & "']"
'            xomPhones=xomRootNode.Query(strQuery)
'            xomPhone=xomPhones.GetNode(0)
'            xomNod=xomPhon
'            Log(strID & " " & strPhone)
            Cal=xomPhone.GetFirstChildElementByName("call").value
            dblCal=Cal+1
            Cal=dblCal
            xomPhone.GetFirstChildElementByName("call").RemoveChildren
            xomPhone.GetFirstChildElementByName("call").AppendChild(Cal)
            xomPhone.GetFirstChildElementByName("time").RemoveChildren
            xomPhone.GetFirstChildElementByName("time").AppendChild(strTime)             
         
            Dim props As Map
            props.Initialize
            props.Put("indent", "yes")
            File.WriteString(File.DirInternal, "/dindin/rating.xml", xomDoc.ToXML)
 
Upvote 0
Top