Android Question How to parse "strange" xml

vecino

Well-Known Member
Licensed User
Longtime User
Hi, a webservice soap returns a strange xml and I can't extract the information.
What can I use to get it?
Thank you very much.

XML:
Expand Collapse Copy
<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:NS1="urn:wsNewGES2000Intf-IwsNewGES2000">
    <NS1:PDA_DatosArticuloResponse xmlns:NS2="urn:wsNewGES2000Intf">
      <NS2:PDADatosArticulo id="1" xsi:type="NS2:PDADatosArticulo">
        <Codigo xsi:type="xsd:string">B1001</Codigo>
        <Nombre xsi:type="xsd:string">PAT. LISA FAMILIAR 260 GRS X 12 B.</Nombre>
        <Existencias xsi:type="xsd:double">0</Existencias>
        <Costo xsi:type="xsd:double">1.2</Costo>
        <Margen0 xsi:type="xsd:double">3</Margen0>
        <PVP0 xsi:type="xsd:double">1.212</PVP0>
        <Margen1 xsi:type="xsd:double">2</Margen1>
        <PVP1 xsi:type="xsd:double">1.224</PVP1>
        <Margen2 xsi:type="xsd:double">0</Margen2>
        <PVP2 xsi:type="xsd:double">1.236</PVP2>
        <Margen3 xsi:type="xsd:double">4</Margen3>
        <PVP3 xsi:type="xsd:double">1.248</PVP3>
      </NS2:PDADatosArticulo>
      <return href="#1"/>
    </NS1:PDA_DatosArticuloResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
 
maybe
B4X:
Expand Collapse Copy
xml = xml.Trim.Replace("NS2:","").Replace("xsi:","").Replace("xsd:","")
before Xml2Map
 
Upvote 0
test:
new: StripNamespaces = True

only use
sXML = sXML.Trim.Replace("xsi:","").Replace("xsd:","")
B4X:
Expand Collapse Copy
    Dim sXML As String = $"
    <?xml version="1.0"?>
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
      <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:NS1="urn:wsNewGES2000Intf-IwsNewGES2000">
        <NS1:PDA_DatosArticuloResponse xmlns:NS2="urn:wsNewGES2000Intf">
          <NS2:PDADatosArticulo id="1" xsi:type="NS2:PDADatosArticulo">
            <Codigo xsi:type="xsd:string">B1001</Codigo>
            <Nombre xsi:type="xsd:string">PAT. LISA FAMILIAR 260 GRS X 12 B.</Nombre>
            <Existencias xsi:type="xsd:double">0</Existencias>
            <Costo xsi:type="xsd:double">1.2</Costo>
            <Margen0 xsi:type="xsd:double">3</Margen0>
            <PVP0 xsi:type="xsd:double">1.212</PVP0>
            <Margen1 xsi:type="xsd:double">2</Margen1>
            <PVP1 xsi:type="xsd:double">1.224</PVP1>
            <Margen2 xsi:type="xsd:double">0</Margen2>
            <PVP2 xsi:type="xsd:double">1.236</PVP2>
            <Margen3 xsi:type="xsd:double">4</Margen3>
            <PVP3 xsi:type="xsd:double">1.248</PVP3>
          </NS2:PDADatosArticulo>
          <return href="#1"/>
        </NS1:PDA_DatosArticuloResponse>
      </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    "$

    sXML = sXML.Trim.Replace("xsi:","").Replace("xsd:","")

    Dim Xml2Map1 As Xml2Map
    Xml2Map1.Initialize
    Xml2Map1.StripNamespaces = True
    Dim mRoot As Map = Xml2Map1.Parse(sXML)
   
    Dim Envelope As Map = mRoot.Get("Envelope")
    Dim Body As Map = Envelope.Get("Body")
    Dim PDA_DatosArticuloResponse As Map = Body.Get("PDA_DatosArticuloResponse")
    Dim PDADatosArticulo As Map = PDA_DatosArticuloResponse.Get("PDADatosArticulo")
   
    Log(PDADatosArticulo.Get("Nombre").As(Map).Get("Text"))
    Log(PDADatosArticulo.Get("Margen0").As(Map).Get("Text"))
1710286391390.png
 
Last edited:
Upvote 0
see:

Note:
xsd: defines the structure of the elements
xsi: namespace prefix
 
Last edited:
Upvote 0
What can I use to get it?

For unnested (single data record) XML I usually just do it "manually" with eg a function like this:
B4X:
Expand Collapse Copy
Sub GetXMLField(XML As String, FieldName As String) As String
    Dim P1 As Int = XML.IndexOf("<" & FieldName & " ")
    If P1 > -1 Then
        Dim P2 As Int = XML.IndexOf2(">", P1)
        If P2 > -1 Then
            Dim P3 As Int = XML.IndexOf("</" & FieldName & ">")
            If P3 > -1 Then
                Return XML.SubString2(P2 + 1, P3)
            End If
        End If
    End If
 
    Return "XML field " & FieldName & " not found"    'or whatever you prefer to signify not found
End Sub

which on your sample data:
B4X:
Expand Collapse Copy
Dim SampleXML As String = $"<?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
  <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:NS1="urn:wsNewGES2000Intf-IwsNewGES2000">
    <NS1:PDA_DatosArticuloResponse xmlns:NS2="urn:wsNewGES2000Intf">
      <NS2:PDADatosArticulo id="1" xsi:type="NS2:PDADatosArticulo">
        <Codigo xsi:type="xsd:string">B1001</Codigo>
        <Nombre xsi:type="xsd:string">PAT. LISA FAMILIAR 260 GRS X 12 B.</Nombre>
        <Existencias xsi:type="xsd:double">0</Existencias>
        <Costo xsi:type="xsd:double">1.2</Costo>
        <Margen0 xsi:type="xsd:double">3</Margen0>
        <PVP0 xsi:type="xsd:double">1.212</PVP0>
        <Margen1 xsi:type="xsd:double">2</Margen1>
        <PVP1 xsi:type="xsd:double">1.224</PVP1>
        <Margen2 xsi:type="xsd:double">0</Margen2>
        <PVP2 xsi:type="xsd:double">1.236</PVP2>
        <Margen3 xsi:type="xsd:double">4</Margen3>
        <PVP3 xsi:type="xsd:double">1.248</PVP3>
      </NS2:PDADatosArticulo>
      <return href="#1"/>
    </NS1:PDA_DatosArticuloResponse>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
"$

and tested using:
B4X:
Expand Collapse Copy
Log("VENDITA!!! --- " & GetXMLField(SampleXML, "Nombre") & " --- VENDITA!!!")

Dim FieldName() As String = Array As String("Codigo", "Nombre", "Existencias", "Costo", "Margen3", "margen3")
For Each S As String In FieldName
    Log(S & " = " & GetXMLField(SampleXML, S))
Next

returns this:
Log output:
Expand Collapse Copy
Waiting for debugger to connect...
Program started.
VENDITA!!! --- PAT. LISA FAMILIAR 260 GRS X 12 B. --- VENDITA!!!
Codigo = B1001
Nombre = PAT. LISA FAMILIAR 260 GRS X 12 B.
Existencias = 0
Costo = 1.2
Margen3 = 4
margen3 = XML field margen3 not found
 
Last edited:
Upvote 0
???
Another way to do it with REGEX.
B4X:
Expand Collapse Copy
    Dim Pattern As String = $"<(?<tagName>\w+)(?<attributes>.*?)>(?<Value>.*)<\/\w+>"$
    Dim XMLMatcher As Matcher = Regex.Matcher(Pattern, SampleXML)
    Do While XMLMatcher.Find
        Log($"${XMLMatcher.Group(1)}: ${XMLMatcher.Group(3)}"$)
    Loop
1710298720567.png
 
Upvote 0
:rolleyes::rolleyes::rolleyes:
Other (Using a map)
EDIT:
B4X:
Expand Collapse Copy
    Dim DataXML As Map : DataXML.Initialize
    Dim Pattern As String = $"<(?<tagName>\w+)(?<attributes>.*?)>(?<Value>.*)<\/\w+>"$
    Dim MatcherXML As Matcher = Regex.Matcher(Pattern, SampleXML)
    Do While MatcherXML.Find
        DataXML.Put(MatcherXML.Group(1), MatcherXML.Group(3))
    Loop

    For Each Key As String In DataXML.Keys
        Log($"${Key}: ${DataXML.Get(Key)}"$)
    Next
   
    Log("------------")

    Log("Nombre " & DataXML.Get("Nombre"))
    Log("Existencias " & DataXML.Get("Existencias"))
    Log("PVP0 " & DataXML.Get("PVP0"))

1710300165148.png
 
Last edited:
Upvote 0
Another way to do it with REGEX.
B4X:
Expand Collapse Copy
Dim Pattern As String = $"<(?<tagName>\w+)(?<attributes>.*?)>(?<Value>.*)<\/\w+>"$

That pattern is a work of art ? and possibly magic too ? but I still can't work out what the third question mark is for.
 
Upvote 0
B4X:
Expand Collapse Copy
Dim Pattern As String = $"<(?<tagName>\w+)(?<attributes>.*?)>(?<Value>.*)<\/\w+>"$

That pattern is a work of art ? and possibly magic too ? but I still can't work out what the third question mark is for.
There are actually four pieces of information that regex collects.
Groups 0 and 2 are the attributes.

I got this routine that I had from an app, where I needed to know the type of the attribute of each field (string, double, etc.)
1710303063370.png


See:
<(?<tagName>\w+)(?<attributes>.*?)>(?<Value>.*)
 
Upvote 0
<(?<tagName>\w+)(?<attributes>.*?)>(?<Value>.*)<\/\w+>

I still can't work out what the third question mark is for. Not that it matters; just curious from a learn-something-new-every-day perspective. ?
 
Upvote 0
<attributes>.*? ==>> it's optional

Using the sign ?

matches the previous token between zero and unlimited times, as few times as possible, expanding as needed (lazy)

Not use
matches the previous token between zero and unlimited times, as many times as possible, giving back as needed (greedy)

Note:
this regex is for matching
<name attributes>value<name>

See
 
Last edited:
Upvote 0
Hi folks, I am overwhelmed by so much valuable information and so many possibilities to get that data extracted.
I am now in a dilemma to decide for one option or another.
That's awesome, thank you so much.
:)
 
Upvote 0
decide for one option or another

Tbh I think a combination would be best for your probable use-case:

B4X:
Expand Collapse Copy
Sub GetXMLField(XML As String, FieldName As String) As String
    Dim Pattern As String = "<" & FieldName & ".*>(.*)<\/"
    Dim XMLMatcher As Matcher = Regex.Matcher(Pattern, XML)
    If XMLMatcher.Find Then
        Return XMLMatcher.Group(1)
    End If
    Return "XML field " & FieldName & " not found"    'or whatever you prefer to signify not found
End Sub

as long as you don't mind having to trust that the regex pattern goobledygook is real programming and not some arbitrary magical incantation. ?
 
Last edited:
Upvote 0
try this:


B4X:
Expand Collapse Copy
    Dim xml As String = $"
    <?xml version="1.0"?>
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/">
      <SOAP-ENV:Body SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:NS1="urn:wsNewGES2000Intf-IwsNewGES2000">
        <NS1:PDA_DatosArticuloResponse xmlns:NS2="urn:wsNewGES2000Intf">
          <NS2:PDADatosArticulo id="1" xsi:type="NS2:PDADatosArticulo">
            <Codigo xsi:type="xsd:string">B1001</Codigo>
            <Nombre xsi:type="xsd:string">PAT. LISA FAMILIAR 260 GRS X 12 B.</Nombre>
            <Existencias xsi:type="xsd:double">0</Existencias>
            <Costo xsi:type="xsd:double">1.2</Costo>
            <Margen0 xsi:type="xsd:double">3</Margen0>
            <PVP0 xsi:type="xsd:double">1.212</PVP0>
            <Margen1 xsi:type="xsd:double">2</Margen1>
            <PVP1 xsi:type="xsd:double">1.224</PVP1>
            <Margen2 xsi:type="xsd:double">0</Margen2>
            <PVP2 xsi:type="xsd:double">1.236</PVP2>
            <Margen3 xsi:type="xsd:double">4</Margen3>
            <PVP3 xsi:type="xsd:double">1.248</PVP3>
          </NS2:PDADatosArticulo>
          <return href="#1"/>
        </NS1:PDA_DatosArticuloResponse>
      </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    "$

    Dim fields() As String = Array As String("Codigo", "Nombre", "Existencias", "Costo", "Margen0", "PVP0", "Margen1", "PVP1", "Margen2", "PVP2", "Margen3", "PVP3")
    Dim valuesMap As Map
    valuesMap.Initialize
    
    For Each field As String In fields
        Dim match As Matcher =     Regex.Matcher("<" & field & ".*?>(.*?)<\/" & field & ">", xml)
        If match.Find Then
            Dim value As String = match.Group(1)
            Log(field & " = " & value)
            valuesMap.Put(field, value)
        End If
    Next
    
    ' valuesMap now contains values
 
Upvote 0
????
All the solutions I've seen are good, but what would happen if the XML contains more than one element with the same name?
g.

XML:
Expand Collapse Copy
<NS2:pDADatosArticulo id="1" xsi:type="NS2:pDADatosArticulo">
......
</NS2:pDADatosArticulo>

<NS2:pDADatosArticulo id="2" xsi:type="NS2:pDADatosArticulo">
......
</NS2:pDADatosArticulo>
 
Last edited:
Upvote 0
Back
Top