github.com/fluhus/gostuff@v0.4.1-0.20240331134726-be71864f2b5d/xmlnode/read.go (about)

     1  // Package xmlnode provides a hierarchical node representation of XML documents.
     2  // This package wraps encoding/xml and can be used instead of it.
     3  //
     4  // Each node has an underlying concrete type, but calling all functions is
     5  // legal. For example, here is how you can traverse the node tree:
     6  //
     7  //	func traverse(n Node) {
     8  //	  // Text() returns an empty string for non-text nodes.
     9  //	  doSomeTextSearch(n.Text())
    10  //
    11  //	  // Children() returns nil for non-parent nodes.
    12  //	  for _, child := range n.Children() {
    13  //	    traverse(child)
    14  //	  }
    15  //	}
    16  package xmlnode
    17  
    18  import (
    19  	"encoding/xml"
    20  	"io"
    21  )
    22  
    23  // ReadAll reads all XML data from the given reader and stores it in a root node.
    24  func ReadAll(r io.Reader) (Node, error) {
    25  	// Create root node.
    26  	// Starting with Tag instead of Root, to eliminate type checks when referring
    27  	// to parent nodes during reading. Will be replaced with a Root node at the
    28  	// end.
    29  	result := &tag{
    30  		nil,
    31  		"",
    32  		nil,
    33  		nil,
    34  	}
    35  	dec := xml.NewDecoder(r)
    36  
    37  	var t xml.Token
    38  	var err error
    39  	current := result
    40  
    41  	// Parse tokens.
    42  	for t, err = dec.Token(); err == nil; t, err = dec.Token() {
    43  		switch t := t.(type) {
    44  		case xml.StartElement:
    45  			// Copy attributes.
    46  			attrs := make([]*xml.Attr, len(t.Attr))
    47  			for i, attr := range t.Attr {
    48  				attrs[i] = &xml.Attr{Name: attr.Name, Value: attr.Value}
    49  			}
    50  
    51  			// Create child node.
    52  			child := &tag{
    53  				current,
    54  				t.Name.Local,
    55  				attrs,
    56  				nil,
    57  			}
    58  
    59  			current.children = append(current.children, child)
    60  			current = child
    61  
    62  		case xml.EndElement:
    63  			current = current.Parent().(*tag)
    64  
    65  		case xml.CharData:
    66  			child := &text{
    67  				current,
    68  				string(t),
    69  			}
    70  
    71  			current.children = append(current.children, child)
    72  
    73  		case xml.Comment:
    74  			child := &comment{
    75  				current,
    76  				string(t),
    77  			}
    78  
    79  			current.children = append(current.children, child)
    80  
    81  		case xml.ProcInst:
    82  			child := &procInst{
    83  				current,
    84  				string(t.Target),
    85  				string(t.Inst),
    86  			}
    87  
    88  			current.children = append(current.children, child)
    89  
    90  		case xml.Directive:
    91  			child := &directive{
    92  				current,
    93  				string(t),
    94  			}
    95  
    96  			current.children = append(current.children, child)
    97  		}
    98  	}
    99  
   100  	// EOF is ok.
   101  	if err != io.EOF {
   102  		return nil, err
   103  	}
   104  
   105  	return &root{result.children}, nil
   106  }