dom - XML Document Object Model

Module Description

The dom module implements a simplified XML Document Object Model. The module reads a XML source into a tree of nodes. The tree can then be iterated and modified. After modification the tree can be written to a XML destination. As with every DOM implementation the tree will use a lot of memory for large XML documents. Keep in mind that tree modifications checks are limited. DTDs are not stored in the tree. Depending on the node type the following stack state is expected by dom-set, dom-append-node, dom-insert-node-before and dom-insert-node-after:

dom.element:   -- c-addr u              = Tag name
dom.attribute: -- c-addr1 u1 c-addr2 u2 = Attribute name c-addr1 u1 and value c-addr2 u2
dom.text:      -- c-addr u              = Normal xml text
dom.cdata:     -- c-addr u              = CDATA section text
dom.pi:        -- c-addr u              = Proc. instr. target c-addr1 u1 and value c-addr2 u2
dom.comment:   -- c-addr n              = Comment
dom.document:  --                       = Document root

Module Words

XML node types

dom.not-used ( -- n )
DOM node: Not used

dom.element ( -- n )
DOM node: Tag

dom.attribute ( -- n )
DOM node: Attribute

dom.text ( -- n )
DOM node: Text

dom.cdata ( -- n )
DOM node: CDATA

dom.entity-ref ( -- n )
DOM node: Entity reference [not used]

dom.entity ( -- n )
DOM node: Entitiy [not used]

dom.pi ( -- n )
DOM node: Processor Instruction

dom.comment ( -- n )
DOM node: Comment

dom.document ( -- n )
DOM node: Start document

dom.doc-type ( -- n )
DOM node: Document type [not used]

dom.doc-fragment ( -- n )
DOM node: Document fragment [not used]

dom.notation ( -- n )
DOM node: Notation [not used]

DOM structure

dom% ( -- n )
Get the required space for a dom variable

DOM creation, initialisation and destruction

dom-init ( dom -- )
Initialise the DOM

dom-(free) ( dom -- )
Free the internal, private variables from the heap

dom-create ( "<spaces>name" -- ; -- dom )
Create a named DOM in the dictionary

dom-new ( -- dom )
Create a new DOM on the heap

dom-free ( dom -- )
Free the DOM from the heap

Iterating the DOM tree

dom-get ( dom -- n true | false )
Get the xml node type of the current node

dom-get-type ( dom -- n )
Get the xml node type of the current node

dom-get-name ( dom -- c-addr u )
Get the name from the current node

dom-get-value ( dom -- c-addr u )
Get the value from the current node

dom-document ( dom -- true | false )
Move the iterator to the document [=root] node

dom-document? ( dom -- flag )
Check if the current node is the document [=root] node

dom-parent ( dom -- n true | false )
Move the iterator to the parent node, return the xml type of this node

dom-children ( dom -- n )
Return the number of children for the current node

dom-children? ( dom -- flag )
Check if the current node has children

dom-child ( dom -- n true | false )
Move the iterator to the first child node, return the xml type of this node

dom-first ( dom -- n true | false )
Move the iterator to the first sibling node, return the xml type of this node

dom-first? ( dom -- flag )
Check if the current node is the first sibling node

dom-next ( dom -- n true | false )
Move the iterator to the next sibling node, return the xml type of this node

dom-prev ( dom -- n true | false )
Move the iterator to the previous sibling node, return the xml type of this node

dom-last ( dom -- n true | false )
Move the iterator to the last sibling node, return the xml type of this node

dom-last? ( dom -- flag )
Check if the current node is the last sibling node

Modifying the DOM tree

dom-set ( i*x dom -- )
Update the current node

dom-append-node ( i*x n dom -- )
Append a node to the current node, exception if not allowed, iterator is moved to the new node

dom-insert-node-before ( i*x n dom -- )
Insert a node before the current node, exception if not allowed

dom-insert-node-after ( i*x n -- )
Insert a node after the current node, exception if not allowed

dom-remove ( dom -- flag )
Remove the current sibling node without children from the tree, iterator is moved to the next, previous or parent node, return the removed node

Reading the DOM tree

dom-read-string ( c-addr u flag1 dom -- flag2 )
Read xml source from the string c-addr u into the dom tree, flag1 indicates whitespace stripping, throw exception if tree is not empty, return success in flag2

dom-read-reader ( x xt flag1 dom -- flag2 )
Read xml source with the reader xt with its state x into the dom tree, flag1 inidicates whitespace stripping, throw exception if tree is not empty, return success in flag2

Writing the DOM tree

dom-write-string ( dom -- c-addr u true | false )
Write the tree to xml returning a string c-addr u if succesfull

dom-write-writer ( x xt dom -- flag )
Write the tree to xml using writer xt and its data x, flag indicate success

Inspection

dom-dump ( dom - )
Dump the DOM tree

Examples

include ffl/dom.fs


\ Example 1: Read xml from file, iterate and write to string


\ Create the xml-dom in the dictionary

dom-create dom1


\ Setup a file reader for the dom

: dom-reader   ( file-id -- c-addr u | 0 = Read the next chunk of the file )
  pad 64 rot read-file throw
  dup IF
    pad swap
  THEN
;


\ Open the source xml file

s" test.xml" r/o open-file throw value dom.input 


\ Read the index.html file with the dom-reader in the dom, leading and trailing whitespace is skipped

dom.input ' dom-reader true dom1 dom-read-reader [IF]
  .( XML File is successfully read ) cr
[ELSE]
  .( XML File is not correct ) cr
[THEN]


\ Iterate in the dom, start with the root, the xml-document

dom1 dom-document [IF]
  .( Iterate the start of the xml document ) cr
[ELSE]
  .( No document start ?? ) cr
[THEN]


\ Move the iterator to the first child of the xml-document

dom1 dom-child [IF]
  dup dom.attribute = [IF]
    drop
    .( Attribute with name: ) dom1 dom-get-name type .(  and value: ) dom1 dom-get-value type cr
  [ELSE]
    dup dom.element = [IF]
      drop
      .( Tag with name: ) dom1 dom-get-name type .(  and value: ) dom1 dom-get-value type cr
    [ELSE]
      dup dom.comment = [IF]
        drop
        .( Comment with value: ) dom1 dom-get-value type cr
      [ELSE]
        dup dom.text = [IF]
          drop
          .( Text with value: ) dom1 dom-get-value type cr
        [ELSE]
          dom.pi = [IF]
            .( Processing instruction with name: ) dom1 dom-get-name type .(  and value: ) dom1 dom-get-value type cr
          [ELSE]
            .( Perhaps a CDATA section ?) cr
          [THEN]
        [THEN]
      [THEN]
    [THEN]
  [THEN]
[ELSE]
  .( xml document has no children.) cr
[THEN]


\ Move the iterator to the next child of the xml-document

dom1 dom-next [IF]
  dup dom.attribute = [IF]
    drop
    .( Attribute with name: ) dom1 dom-get-name type .(  and value: ) dom1 dom-get-value type cr
  [ELSE]
    dup dom.element = [IF]
      drop
      .( Tag with name: ) dom1 dom-get-name type .(  and value: ) dom1 dom-get-value type cr
    [ELSE]
      dup dom.comment = [IF]
        drop
        .( Comment with value: ) dom1 dom-get-value type cr
      [ELSE]
        dup dom.text = [IF]
          drop
          .( Text with value: ) dom1 dom-get-value type cr
        [ELSE]
          dom.pi = [IF]
            .( Processing instruction with name: ) dom1 dom-get-name type .(  and value: ) dom1 dom-get-value type cr
          [ELSE]
            .( Perhaps a CDATA section ?) cr
          [THEN]
        [THEN]
      [THEN]
    [THEN]
  [THEN]
[ELSE]
  .( xml document has no more children.) cr
[THEN]


\ Write the xml

dom1 dom-write-string [IF]
  .( xml document: ) type cr
[ELSE]
  .( Problems writing the xml document.)  cr
[THEN]
[THEN]



\ Example 2:  Read xml from string and write to a file


\ Create the xml-dom on the heap

dom-new value dom2


\ Read xml from a string, skipping any leading and trailing whitespace

s" <?xml version='1.1'?>  <!-- test -->  <car>  <color>  blue  </color>  </car>" true dom2 dom-read-string [IF] 
  .( XML is sucessfully read.) cr
[ELSE]
  .( XML was not correct.) cr
[THEN]


\ Write the xml-dom to a file using a writer

: dom-writer  ( c-addr u file-id -- flag = Write the xml using a writer )
  write-file throw
  true
;


\ Open the file for the writer

s" out.xml" w/o create-file throw value dom.output


\ Write the xml-dom to the writer

dom.output ' dom-writer dom2 dom-write-writer [IF]
  .( XML is successfully written.) cr
[ELSE]
  .( Problems writing the xml-dom.) cr
[THEN]


\ Free the dom from the heap

dom2 dom-free


\ Example 3: build a xml document from scratch using the xml-dom


\ Create the xml-dom on the heap

dom-new value dom3


\ Start with the root, the xml-document

dom.document dom3 dom-append-node


\ Add the version attribute to the xml-document

s" version" s" 1.0" dom.attribute dom3 dom-append-node


\ Move back to the xml-document and add a tag

dom3 dom-parent 2drop
s" tag" dom.element dom3 dom-append-node


\ Add text to the element

s" hello" dom.text dom3 dom-append-node


\ Write the xml to a string

dom3 dom-write-string [IF]
  .( XML successfully written: ) type cr
[ELSE]
  .( Problems...) cr
[THEN]


\ Free the dom from the heap

dom3 dom-free

generated 10-Apr-2008 by ofcfrth-0.5.0