Page 6 of 9
Posted: Mon Jan 15, 2007 3:14 pm
by davenull
Flype wrote:Here is a small example to introduce PureXML2.
This shows how to very easily retrieves partial informations
from a distant XML document using ParseUrl() and the CharacterData handler.
Code: Select all
Global result$
Procedure.l Extract_From_IpPages(name.s, string.s, length.l)
Select name ; tag name
Case "ip": result$ + "IP V4: " + #TAB$ + string + #CRLF$
Case "ip_long": result$ + "IP: " + #TAB$ + string + #CRLF$
Case "ipv6": result$ + "IP V6: " + #TAB$ + string + #CRLF$
Case "host": result$ + "HOST: " + #TAB$ + string + #CRLF$
Case "isp": result$ + "ISP: " + #TAB$ + string + #CRLF$
Case "country": result$ + "CTRY: " + #TAB$ + string + #CRLF$
EndSelect
EndProcedure
If MessageRequester("Question", "Resolves your IP Address from `ippages.com` ?", #MB_OKCANCEL|#MB_ICONQUESTION) = #IDOK
PureXML_SetCharacterDataHandler(@Extract_From_IpPages())
If PureXML_ParseUrl("http://www.ippages.com/xml/")
If MessageRequester("Copy the result to the clipboard ?", result$, #MB_OKCANCEL|#MB_ICONINFORMATION) = #IDOK
SetClipboardText(result$)
EndIf
Else
MessageRequester("Error", PureXML_GetErrorString(), #MB_ICONERROR)
EndIf
EndIf
This is a very practical way to parse XML files. Similar approach is also used in perl. Thank you very much for making this library available for Purebasic community. I'll be using it very much and believe it's a heaven-sent library for many developers.
-davenull-
Posted: Mon Jan 15, 2007 3:32 pm
by Flype
thank you davenull, nice to you.
i hope it will useful for many developpers.
i need volonteers for beta testing, writing full examples...
Posted: Mon Jan 15, 2007 3:38 pm
by davenull
Flype wrote:thank you davenull, nice to you.
i hope it will useful for many developpers.
i need volonteers for beta testing, writing full examples...
I'll test this library starting tomorrow and post my experiences here.
-davenull-
Posted: Tue Jan 16, 2007 3:34 pm
by davenull
I'll test this library starting tomorrow and post my experiences here.
It's been a busy day, so only little testing has been possible.
I tried to parse an xml file with an empty value and the parser didn't detect it.
Here's the test code:
Code: Select all
Macro PrintXMLData(xml_tag)
If tagname = xml_tag
If chardata = ""
Debug "Empty!"
Else
Debug chardata
EndIf
EndIf
EndMacro
Procedure ProductInfo(tagname.s, chardata.s, length.l)
PrintXMLData("name")
PrintXMLData("price")
PrintXMLData("desc")
EndProcedure
PureXML_SetCharacterDataHandler(@ProductInfo())
PureXML_ParseFile("products.xml")
And this is the xml file (products.xml):
<?xml version="1.0" encoding="ISO-8859-1"?>
<products>
<product>
<name>gizmo</name>
<price>50</price>
<desc>for geeks</desc>
</product>
<product>
<name>gadget</name>
<price>10</price>
<desc>for programmers</desc>
</product>
<product>
<name>utility</name>
<price>99</price>
<desc></desc>
</product>
</products>
The description (<desc></desc>) of the last product is empty, so the debug line should read "Empty!". It doesn't print anything, however. Is this the way it should be or how to detect the empty values?
-davenull-
Posted: Tue Jan 16, 2007 8:21 pm
by Flype
The CharacterData Handler isn't called if there's no data to transmit, so the behaviour is normal in my opinion.
The easier way in your case (and many others cases) is to catch the data you want once the tag is closed which means totally parsed (open-chardata-close).
i'll prepare you an example right now.
Posted: Tue Jan 16, 2007 8:31 pm
by Flype
Code: Select all
Procedure ProductInfo(name.s, depth.l)
Protected chardata.s
Select name
Case "name", "price", "desc"
chardata = PureXML_GetCharData()
If Trim(chardata)
Debug name + " = " + chardata
Else
Debug name + " = Empty !"
EndIf
Case "product"
Debug ""
EndSelect
EndProcedure
PureXML_SetCloseHandler(@ProductInfo())
PureXML_ParseFile("products.xml")
Posted: Tue Jan 16, 2007 8:42 pm
by Flype
Here is a full example that shows how to add all your Products in a LinkedList ready for classic processing :
xml$ + "<?xml version='1.0'?>"
xml$ + "<products>"
xml$ + " <product id='PROD007'>"
xml$ + " <name>gizmo</name>"
xml$ + " <price>50</price>"
xml$ + " <desc>for geeks</desc>"
xml$ + " </product>"
xml$ + " <product id='PROD008'>"
xml$ + " <name>gadget</name>"
xml$ + " <price>10</price>"
xml$ + " <desc>for programmers</desc>"
xml$ + " </product>"
xml$ + " <product id='PROD009'>"
xml$ + " <name>utility</name>"
xml$ + " <price>99</price>"
xml$ + " <desc></desc>"
xml$ + " </product>"
xml$ + "</products>"
Code: Select all
Structure PRODUCT
id.s
name.s
price.s
desc.s
EndStructure
Global NewList product.PRODUCT()
Procedure ProductInfo_StartElement(name.s, depth.l)
If name = "product"
If AddElement(product())
product()\id = PureXML_GetAttr("id")
EndIf
EndIf
EndProcedure
Procedure ProductInfo_EndElement(name.s, depth.l)
Select name
Case "name": product()\name = PureXML_GetCharData()
Case "price": product()\price = PureXML_GetCharData()
Case "desc": product()\desc = PureXML_GetCharData()
EndSelect
EndProcedure
PureXML_SetElementHandler(@ProductInfo_StartElement(), @ProductInfo_EndElement())
If PureXML_ParseString(xml$)
ForEach product()
Debug "id = " + product()\id
Debug "name = " + product()\name
Debug "price = " + product()\price
Debug "desc = " + product()\desc
Debug ""
Next
EndIf
Posted: Wed Jan 17, 2007 8:28 am
by davenull
Flype wrote:Here is a full example that shows how to add all your Products in a LinkedList ready for classic processing :
xml$ + "<?xml version='1.0'?>"
xml$ + "<products>"
xml$ + " <product id='PROD007'>"
xml$ + " <name>gizmo</name>"
xml$ + " <price>50</price>"
xml$ + " <desc>for geeks</desc>"
xml$ + " </product>"
xml$ + " <product id='PROD008'>"
xml$ + " <name>gadget</name>"
xml$ + " <price>10</price>"
xml$ + " <desc>for programmers</desc>"
xml$ + " </product>"
xml$ + " <product id='PROD009'>"
xml$ + " <name>utility</name>"
xml$ + " <price>99</price>"
xml$ + " <desc></desc>"
xml$ + " </product>"
xml$ + "</products>"
Code: Select all
Structure PRODUCT
id.s
name.s
price.s
desc.s
EndStructure
Global NewList product.PRODUCT()
Procedure ProductInfo_StartElement(name.s, depth.l)
If name = "product"
If AddElement(product())
product()\id = PureXML_GetAttr("id")
EndIf
EndIf
EndProcedure
Procedure ProductInfo_EndElement(name.s, depth.l)
Select name
Case "name": product()\name = PureXML_GetCharData()
Case "price": product()\price = PureXML_GetCharData()
Case "desc": product()\desc = PureXML_GetCharData()
EndSelect
EndProcedure
PureXML_SetElementHandler(@ProductInfo_StartElement(), @ProductInfo_EndElement())
If PureXML_ParseString(xml$)
ForEach product()
Debug "id = " + product()\id
Debug "name = " + product()\name
Debug "price = " + product()\price
Debug "desc = " + product()\desc
Debug ""
Next
EndIf
Very nice, thanks. This method is quite usable.
-davenull-
Posted: Wed Jan 17, 2007 9:09 am
by Flype
Code: Select all
Procedure ProductInfo_EndElement(name.s, depth.l)
Select name
Case "name": product()\name = PureXML_GetCharData()
Case "price": product()\price = PureXML_GetCharData()
Case "desc": product()\desc = PureXML_GetCharData()
EndSelect
EndProcedure
i think i will add PureXML_GetCharData() directly in the argument list (in the EndElement handler.
so that it might be easier to process the chardata - perhaps faster :
Code: Select all
Procedure ProductInfo_EndElement(name.s, chardata.s, length.l, depth.l)
Select name
Case "name": product()\name = chardata
Case "price": product()\price = chardata
Case "desc": product()\desc = chardata
EndSelect
EndProcedure
in the next beta4...
and i'm not sure that the 'depth.l' argument is really useful - should i remove it ?
Posted: Wed Jan 17, 2007 9:57 am
by davenull
Flype wrote:Code: Select all
Procedure ProductInfo_EndElement(name.s, depth.l)
Select name
Case "name": product()\name = PureXML_GetCharData()
Case "price": product()\price = PureXML_GetCharData()
Case "desc": product()\desc = PureXML_GetCharData()
EndSelect
EndProcedure
i think i will add PureXML_GetCharData() directly in the argument list (in the EndElement handler.
so that it might be easier to process the chardata - perhaps faster :
Code: Select all
Procedure ProductInfo_EndElement(name.s, chardata.s, length.l, depth.l)
Select name
Case "name": product()\name = chardata
Case "price": product()\price = chardata
Case "desc": product()\desc = chardata
EndSelect
EndProcedure
in the next beta4...
and i'm not sure that the 'depth.l' argument is really useful - should i remove it ?
IMHO, it could be useful to have the 'depth.l' argument, since there could be tags with the same name in different depths.
Are you planning to include the namespace support in the library?
-davenull-
Posted: Wed Jan 17, 2007 10:47 am
by Flype
ok i keep the 'depth' argument.
by 'namespace' you means DTD ?
i'm not familiar at all with namespace - can you explain a bit more ?
but it should be added soon because the Expat library support it.
Posted: Wed Jan 17, 2007 11:06 am
by davenull
Flype wrote:ok i keep the 'depth' argument.
by 'namespace' you means DTD ?
i'm not familiar at all with namespace - can you explain a bit more ?
but it should be added soon because the Expat library support it.
Thanks for enhancing the lib.
W3schools.com offers a nice explanation about namespaces:
http://www.w3schools.com/xml/xml_namespaces.asp
So it might come handy sometimes to have namespaces supported. A nice feature of a event-driven parser is that there's no need to obey a DTD or an XML Schema
-davenull-
ASCII
Posted: Wed Jan 17, 2007 11:28 am
by davenull
Does the current lib support US-ASCII only or also extensions like ISO 8859-1? I was parsing was a tag with an character > 127 in ASCII table (ö) and that didn't work.
-davenull-
Posted: Wed Jan 17, 2007 12:03 pm
by Flype
Yes, there is the PureXML_SetEncoding(encoding.s) which should be initialized before calling PureXML_ParseFile().
It should works.
[edit] thank you the link about namespace - i will take a closer look.
Posted: Wed Jan 17, 2007 12:49 pm
by davenull
Flype wrote:Yes, there is the PureXML_SetEncoding(encoding.s) which should be initialized before calling PureXML_ParseFile().
It should works.
[edit] thank you the link about namespace - i will take a closer look.
Unfortunately I can't get it working. Here's the code you posted earlier with a minor change (gizmo --> gizmö) and of course PureXML_SetEncoding("ISO-8859-1").
Code: Select all
xml$ + "<?xml version='1.0'?>"
xml$ + "<products>"
xml$ + " <product id='PROD007'>"
xml$ + " <name>gizmö</name>"
xml$ + " <price>50</price>"
xml$ + " <desc>for geeks</desc>"
xml$ + " </product>"
xml$ + " <product id='PROD008'>"
xml$ + " <name>gadget</name>"
xml$ + " <price>10</price>"
xml$ + " <desc>for programmers</desc>"
xml$ + " </product>"
xml$ + " <product id='PROD009'>"
xml$ + " <name>utility</name>"
xml$ + " <price>99</price>"
xml$ + " <desc></desc>"
xml$ + " </product>"
xml$ + "</products>"
Structure PRODUCT
id.s
name.s
price.s
desc.s
EndStructure
Global NewList product.PRODUCT()
Procedure ProductInfo_StartElement(name.s, depth.l)
If name = "product"
If AddElement(product())
product()\id = PureXML_GetAttr("id")
EndIf
EndIf
EndProcedure
Procedure ProductInfo_EndElement(name.s, depth.l)
Select name
Case "name": product()\name = PureXML_GetCharData()
Case "price": product()\price = PureXML_GetCharData()
Case "desc": product()\desc = PureXML_GetCharData()
EndSelect
EndProcedure
PureXML_SetEncoding("ISO-8859-1")
PureXML_SetElementHandler(@ProductInfo_StartElement(), @ProductInfo_EndElement())
If PureXML_ParseString(xml$)
ForEach product()
Debug "id = " + product()\id
Debug "name = " + product()\name
Debug "price = " + product()\price
Debug "desc = " + product()\desc
Debug ""
Next
EndIf
It doesn't return an 'o' with umlaut, but rather a capital 'A' with a tilde above plus a paragraph sign (Chr(182)).
Am I doing something wrong?
-davenull-