B4A Library [lib] XOM

This is a partial implementation of the XOM library by by Elliotte Rusty Harold.

XOM™ is a new XML object model. It is an open source (LGPL), tree-based API for processing XML with Java that strives for correctness, simplicity, and performance, in that order.

The library implements all XOM read methods, it's also possible to write XML using this library but not all XOM write methods have (so far) been implemented.

So with B4A we have the existing XmlSax parser which is what's know as a push parser.
XmlSax will parse an XML document from start to end and generate an event each time it finds a start or end element.

XOM let's you access an XML document using DOM methods.
You can access any elements in any order with no requirement to parse the entire document first.
The library has abstract objects that represent various XML elements - you work with objects such as XOMAttribute and XOMNode.

A code example explains it better:

B4X:
Sub Process_Globals
End Sub 

Sub Globals
   Dim CurrentCondition As XOMElement
   Dim DateSpinner As Spinner
   Dim MinTempLabel As Label
   Dim MaxTempLabel As Label
   Dim PrecipitationLabel As Label
   Dim WeatherDescLabel As Label
   Dim WeatherElements As XOMElements
   Dim WindDirectionLabel As Label
   Dim WindSpeedLabel As Label
End Sub 

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("Main")
   
   Dim XOMBuilder1 As XOMBuilder
   XOMBuilder1.Initialize("XOMBuilder1")
   
   '   the BuildFromString method is used here as the BuildFromFile method cannot access files in assets
   Dim XmlString As String=File.GetText(File.DirAssets, "weather.xml")
   XOMBuilder1.BuildFromString(XmlString, "", Null)
   
End Sub 

Sub Activity_Resume
End Sub 

Sub Activity_Pause (UserClosed As Boolean)
End Sub 

Sub DateSpinner_ItemClick (Position As Int, Value As Object)
   If Position=0 Then
     MaxTempLabel.Text="Temp: "&CurrentCondition.GetFirstChildElementByName("temp_C").Value&"C"
     MinTempLabel.Text="Time: "&CurrentCondition.GetFirstChildElementByName("observation_time").Value
     PrecipitationLabel.Text="Precipitation: "&CurrentCondition.GetFirstChildElementByName("precipMM").Value&"mm"
     WeatherDescLabel.Text="Summary: "&CurrentCondition.GetFirstChildElementByName("weatherDesc").Value
     WindDirectionLabel.Text="Wind direction: "&CurrentCondition.GetFirstChildElementByName("winddir16Point").Value
     WindSpeedLabel.Text="Wind speed: "&CurrentCondition.GetFirstChildElementByName("windspeedMiles").Value&"mph"
   Else
     Dim SelectedWeatherElement As XOMElement
     SelectedWeatherElement=WeatherElements.GetElement(Position-1)
     
     MaxTempLabel.Text="Max temp: "&SelectedWeatherElement.GetFirstChildElementByName("tempMaxC").Value&"C"
     MinTempLabel.Text="Min temp: "&SelectedWeatherElement.GetFirstChildElementByName("tempMinC").Value&"C"
     PrecipitationLabel.Text="Precipitation: "&SelectedWeatherElement.GetFirstChildElementByName("precipMM").Value&"mm"
     WeatherDescLabel.Text="Summary: "&SelectedWeatherElement.GetFirstChildElementByName("weatherDesc").Value
     WindDirectionLabel.Text="Wind direction: "&SelectedWeatherElement.GetFirstChildElementByName("winddirection").Value
     WindSpeedLabel.Text="Wind speed: "&SelectedWeatherElement.GetFirstChildElementByName("windspeedMiles").Value&"mph"
   End If
End Sub 

Sub XOMBuilder1_BuildDone(XOMDocument1 As XOMDocument, Tag As Object)
   If XOMDocument1.IsInitialized Then
     Log("XOMDocument is initialized")
     Dim RootElement As XOMElement
     RootElement=XOMDocument1.RootElement
     
     DateSpinner.Add("Current conditions")
     CurrentCondition=RootElement.GetFirstChildElementByName("current_condition")
     
     WeatherElements=RootElement.GetChildElementsByName("weather")
     Dim i, WeatherElementsCount As Int
     WeatherElementsCount=WeatherElements.Size
     For i=0 To WeatherElementsCount-1
       DateSpinner.Add(WeatherElements.GetElement(i).GetFirstChildElementByName("date").Value)
     Next
     
     DateSpinner.Enabled=True
     DateSpinner_ItemClick(0, Null)
   Else
     '   XOMDocument1 will be uninitialized if an error has occurred
     Log("An error has occured and the XOMDocument has NOT been initialized")
     Log(LastException.Message)
   End If
End Sub

The example creates an XOMDocument from a String obtained from a file in Assets.
It extracts the 'current_condition' element and then the 'weather' elements.
These elements are saved as Global XOMElement and XOMElements objects, the rest of the XOMDocument is discarded.

XOM can build an XOMDocument from File, InputStream and Url as well as from a String.
Use of the BuildFromURL method requires android.permission.INTERNET.
XOM does NOT automatically enable this permission, so if you use BuildFromUrl you must ensure that your application has this permission enabled.
I decided this was better than automatically enabling the internet permission - if you do not use BuildFromUrl then your application will not enable an unnecessary permission.


Use of XOM requires two additional jar files to be added to your B4A additional libraries folder: xom-1.2.10.jar and dtd-xercesImpl.jar, the forum attachment size limit prevents me from attaching these two files to this post so i have made them available from here.

I am not actively developing this library, it's an old project that i have uploaded to enable another forum member to extract data from an HTML webpage.
I'll fix any bugs but do not plan to implement any new features.
Source code is available on request if any other forum members want to further develop or modify the library.

Martin.
 

Attachments

  • XOM_example_project.zip
    8.7 KB · Views: 730
  • XOM_library_files_v1.20.zip
    36.7 KB · Views: 985
Last edited:

warwound

Expert
Licensed User
Longtime User
What does this log:

B4X:
Sub XOMBuilder1_BuildDone(XOMDoc As XOMDocument, Tag As Object)
Log(LastException.Message)
If XOMDoc.IsInitialized Then
 

Valeriy Lakhtin

Member
Licensed User
Log(LastException.Message)
I inserted a row
B4X:
Log(LastException.Message)
but failed. Message appears in the log
java.lang.RuntimeException: Object should first be initialized (Exception).
Probably need previously to create and initialize "LastException" object?
 

warwound

Expert
Licensed User
Longtime User
This will log the exception if it exists:

B4X:
Sub XOMBuilder1_BuildDone(XOMDoc As XOMDocument, Tag As Object)
If LastException.IsInitialized Then
 Log(LastException.Message)
End If

BUT if LastException is not initialized that implies that no error has occurred while trying to build the XOMDocument.
Are you 100% sure that your XML is valid?
 

Valeriy Lakhtin

Member
Licensed User
Now everything works OK until I understand what it depends on. I tried to activate the other libraries and take-off lib. Main all OK.
Poor that no one knows what the latest version XOM works well with the latest version of the Android
 

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. Now other function is working but i cant do the sorting elements using XPath query. 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 sorted list of "comp" or sorted by "rating". Example code not work in B4A
B4X:
strQuery="//comp order by /rating"
xomCompanys=xomRootDoc.Query(strQuery)
or
B4X:
strQuery="//comp order by @nam"
xomCompanys=xomRootDoc.Query(strQuery)
Where is my mistake?
 
Last edited:

Ydm

Active Member
Licensed User
Longtime User
This issue may have been written earlier. But I could not find how to write.
I have an XML file. "Number" value element which is how I select 3?

B4X:
    <lyric default-y="-92" justify="left" number="1">
          <syllabic>single</syllabic>
          <text font-size="7" font-weight="bold">1</text>
        </lyric>
        <lyric default-y="56" justify="left" number="3" placement="above">
          <syllabic>single</syllabic>
          <text font-family="Notes" font-size="15.7" font-weight="bold">E</text>
        </lyric>
        <lyric default-y="72" justify="left" number="4" placement="above">
          <syllabic>single</syllabic>
          <text font-family="Notes" font-size="15.7" font-weight="bold">1</text>
          <extend type="start"/>
        </lyric>

Attribute= number And number=3

How can I choose?
 
Last edited:

Valeriy Lakhtin

Member
Licensed User
The easiest way to solve the problem using. standard methods
<code>
strQuery="//lyric [@number='3']"
xomTarget=xomRootDoc.Query(strQuery)
</code>
 

Ydm

Active Member
Licensed User
Longtime User
Thanks. However, the query process is very slow. So if and else usage.
 

Lakhtin_V

Active Member
Licensed User
Longtime User
Good day,
I'm trying to create a program for iOS using the B4X programming language
I successfully used the XOM library for Android application, But when I did in the version for iOS your XOM library does not work. Help please, maybe you have a version of your library for use in iOS? What will you advice me
 

warwound

Expert
Licensed User
Longtime User
I successfully used the XOM library for Android application, But when I did in the version for iOS your XOM library does not work. Help please, maybe you have a version of your library for use in iOS? What will you advice me

XOM is a java library and i'm pretty sure b4i libraries cannot be created from java libraries.
I'm not at all familiar with b4i so could be wrong - hopefully someone else can clarify things.
 
Top