外观
xml
原文: https://finproducts.atlassian.net/wiki/spaces/JIDD3/pages/34793652649/FIN+5.3.1+xml 中文整理基于 FIN 5.3.1
导读: XML解析器和文档建模
以下为 API 参考,保留英文以便对照源码;目录已列出(英文标题便于检索);概述已尽量中译。
目录(导读)
- classes
- enums
- errs
- docs
- Overview
- DOM
- Namespaces
- Writing
- Parsing
- Slot Details
- make
- makeNs
- name
- ns
- prefix
- qname
- toStr
- uri
- val
- write
- add
- docType
- nodeType
- pis
- removePi
- root
- publicId
- rootElem
- systemId
- addAttr
- attr
- attrs
- children
- clearAttrs
- copy
- each
- eachAttr
- elem
- elems
- get
- line
- remove
- removeAt
- removeAttr
- removeAttrAt
- text
- col
- doc
- parent
- writeToStr
- elemEnd
- elemStart
- fromStr
- pi
- vals
- equals
- hash
- isDefault
- close
- depth
- elemAt
- next
- parseDoc
- parseElem
- skip
- target
- cdata
API 正文(英文)
errs
| XErr | XML exception. |
| XIncompleteErr | Incomplete document exception indicates that the end of stream was reached before the end of the document was parsed. |
docs
Overview
The xml API provides the core APIs for working with XML:
XElem: Provides a standard representation of an XML element tree to be used in memory. It is similar to the W3's DOM, but Fantom centric.XParser: is a non-validating XML parser. It may be used in two modes: to read an entire XML document into memory or as a pull-parser.
The features supported by XParser:
- All element, attribute, processing instructions, and character data productions are supported
- CDATA sections are supported
- Namespaces are supported at both the element and attribute level
- Doctype declarations provide access to the public and system identifiers
- DTDs are ignored (as such you can't use internal or external entity declarations)
- No access to comments is provided by the XML parser
- Character data consisting only of whitespace is always ignored
DOM
XML documents are modeled in memory using the following classes:
XDoc: models the entire document providing access to the root element, doctype, and processing instructions declared before the root elementXElem: models an element name, attributes, and children nodesXText: models character data in an elementXPi: models a processing instructionXAttr: models an attribute name/value pairXNs: models the prefix and URI of a XML namespace
XML documents are structured as a tree of nodes using the classes listed above. Typically the tree is built by parsing a XML document from an input stream. But you can construct a document in memory using the APIs and it-blocks:
doc := XDoc
{
XElem("root")
{
XElem("a") { addAttr("flags", "0"); XText("blah"), },
XElem("b") { XElem("c"), },
XElem("m") { XText("I "), XElem("i") { XText("mean"), }, XText(" it!"), },
},
}
doc.write(Env.cur.out)
// would print the following to the console
<?xml version='1.0' encoding='UTF-8'?>
<root>
<a flags='0'>blah</a>
<b>
<c/>
</b>
<m>I <i>mean</i> it!</m>
</root>Namespaces
The XNs class is used model XML namespaces. The default namespace is indicated with the prefix of "". Both XElem and XAttr define a set of methods for working with qualified names:
ns := XNs("x", `urn:foo`)
e := XElem("name", ns)
e.ns => ns
e.prefix => "x"
e.uri => `urn:foo`
e.name => "name"
e.qname => "x:name"If you construct a document by hand, then you are responsible for creating each element with the correct XNs and ensuring that the appropriate namespace attributes are declared:
nsDef := XNs("", `http://foo/default`)
nsBar := XNs("b", `http://foo/bar`)
root := XElem("root", nsBar)
{
XAttr.makeNs(nsDef),
XAttr.makeNs(nsBar),
XElem("elem", nsDef),
XElem("elem", nsBar),
}
root.write(Env.cur.out)
// would print the following to the console
<b:root xmlns='http://foo/default' xmlns:b='http://foo/bar'>
<elem/>
<b:elem/>
</b:root>Writing
All the XML node classes support a write method which takes an OutStream. During debugging it is often convenient to write to standard out:
node.write(Env.cur.out)
Env.cur.out.flushTo write an XML document from memory to a file:
out := `test.xml`.toFile.out
doc.write(out)
out.closeIf you are generating an XML document on the fly you might want to use OutStream directly. It supports escaping XML control characters via the writeXml method:
// escape markup in text
out.writeChars("<text>").writeXml(text).writeChars("</text>")
// escape markup and quotes
out.writeChars("attr='").writeXml(attrVal, OutStream.xmlEscQuotes).writeChars("'")You can use Str.toXml to generate a string with XML markup escaped. However, you should prefer OutStream when streaming which is more efficient.
Parsing
The XParser class is used to parse XML input streams into XElems. The easiest way to do this is to parse the entire document into memory using the parseDoc method:
// parse and close input stream
doc := XParser(in).parseDoc
// parse a file
doc := XParser(`test.xml`.toFile.in).parseDoc
// parse a string
doc := XParser("<foo/>".in).parseDocThe code above parses the document entirely into memory. This tends to be easiest way to work with XML documents. However it can create efficiency problems when parsing large documents, especially when mapping the XElems into other data structures. To support more efficient parsing of XML streams, XParser may also be used to read elements off the input stream one at a time. This is similar to the SAX API, except you pull events instead of having them pushed to you.
To perform pull parsing, use the next method to iterate through the document. This tokenizes the stream into XElem, XText, and XPi chunks. Each call to next advances to the next token and returns its node type. You may also check the type of the current token using nodeType. You may access the current token using elem, text, or pi.
XParser maintains a stack of XElems for you from the root element down to the current element. You may check the depth of the stack using the depth method. Get the current element at any position in the stack via elemAt.
It is very important to understand the XElem at given depth is only valid until the parser returns elemEnd for that depth. After that the element will be reused. A XText instance is only valid until the next call to next. You can make a safe copy of nodes using copy. You can also use parseElem to read the current element and its is descendants into memory during pull-parsing.
Example of pull parsing:
parser := XParser(
"<root>
<a>text</a>
</root>".in)
while (parser.next != null)
echo("$parser.nodeType $parser.depth $parser.elem")
// prints the following to the console
elemStart 0 <root>
elemStart 1 <a>
text 1 <a>
elemEnd 1 <a>
elemEnd 0 <root>XAttr
@Js const class XAttr : Obj
XAttr models an XML attribute in an element. Attributes are immutable and may be shared across multiple XElem parents.
Constructors
| make | Construct an element with unqualified local name, value, and optional XML namespace. |
| makeNs | Construct an attribute which defines a namespace with "xmlns:<prefix>" name and uri value. |
Fields
| name | Unqualified local name of the attribute. |
| ns | The XML namespace which qualified this attribute's name. |
| val | Value of the attribute. |
Methods
| prefix | If this attribute is qualified by an XML namespace then return the namespace's prefix. |
| qname | Qualified name of the attribute. |
| toStr | Return this attribute name/value pair as string. |
| uri | If this element is qualified by an XML namespace then return the namespace's uri. |
| write | Write this attribute to the output stream. |
Slot Details
make
new make(Str name, Str val, XNs? ns := null)
Construct an element with unqualified local name, value, and optional XML namespace. The XNs instance should be defined as an attribute on an ancestor element. Throw ArgErr if an attempt is made to qualify the attribute by the default namespace with prefix of "".
makeNs
new makeNs(XNs ns)
Construct an attribute which defines a namespace with "xmlns:<prefix>" name and uri value. If prefix is "" then construct the default namespace attribute named "xmlns".
name
const Str name
Unqualified local name of the attribute. If an XML namespace prefix was specified, then this is everything after the colon:
foo='val' => foo
x:foo='val' => fooNote that attributes which start with "xml:" and "xmlns:" are not treated as a namespace:
xml:lang='en' => xml:lang
XML:lang='en' => xml:langns
const XNs? ns
The XML namespace which qualified this attribute's name. If the attribute name is unqualified return null.
prefix
Str? prefix()
If this attribute is qualified by an XML namespace then return the namespace's prefix. Otherwise return null. Note an attribute can never be qualified by the default namespace.
qname
Str qname()
Qualified name of the attribute. This is the full name including the XML namespace prefix:
foo='val' => foo
x:foo='val' => x:footoStr
virtual override Str toStr()
Return this attribute name/value pair as string.
uri
Uri? uri()
If this element is qualified by an XML namespace then return the namespace's uri. Otherwise return null.
val
const Str val
Value of the attribute.
write
Void write(OutStream out)
Write this attribute to the output stream.
XDoc
@Js class XDoc : XNode
XML document encapsulates the root element and document type.
Constructors
| make | Construct with optional root elem. |
Fields
| docType | Document type declaration or null if undefined. |
| root | Root element. |
Methods
| add | Add a node to the document. |
| nodeType | Return the XNodeType.doc. |
| pis | Get any processing instructions declared before the root element. |
| removePi | Remove the processing instruction by reference. |
| toStr | Return string representation. |
| write | Write this node to the output stream. |
Slot Details
add
@Operator This add(Obj child)
Add a node to the document. If the node is an XElem then it is defined as the root element, otherwise the child must be a XPi. Return this.
docType
XDocType? docType
Document type declaration or null if undefined.
make
new make(XElem? root := null)
Construct with optional root elem.
nodeType
virtual override XNodeType nodeType()
Return the XNodeType.doc.
pis
XNode[] pis()
Get any processing instructions declared before the root element. Processing instructions after the root are not supported.
removePi
XPi? removePi(XPi pi)
Remove the processing instruction by reference.
root
XElem root
Root element.
toStr
virtual override Str toStr()
Return string representation.
write
virtual override Void write(OutStream out)
Write this node to the output stream.
XDocType
@Js class XDocType : Obj
XML document type declaration (but not the whole DTD).
Fields
| publicId | Public ID of an external DTD or null. |
| rootElem | Element name of of the document. |
| systemId | System ID of an external DTD or null. |
Methods
| toStr | Return string representation of this processing instruction. |
Slot Details
publicId
Str? publicId
Public ID of an external DTD or null.
rootElem
Str rootElem := "undefined"
Element name of of the document.
systemId
Uri? systemId
System ID of an external DTD or null.
toStr
virtual override Str toStr()
Return string representation of this processing instruction.
XElem
@Js class XElem : XNode
Models an XML element: its name, attributes, and children nodes.
Constructors
| make | Construct an element with unqualified local name and optional XML namespace. |
Fields
| line | Line number of XML element in source file or zero if unknown. |
| name | Unqualified local name of the element. |
| ns | The XML namespace which qualified this element's name. |
Methods
| add | If child is a XAttr then add an attribute. |
| addAttr | Add an attribute to this element. |
| attr | Get an attribute by its non-qualified local name. |
| attrs | Get this element's attributes as a readonly list. |
| children | Get this element's children elements, text, and PIs as a readonly list. |
| clearAttrs | Remove all the attributes. |
| copy | Make a shallow copy of this element. |
| each | Iterate each child element, text, and PI node in the children list. |
| eachAttr | Iterate each attribute in the attrs list. |
| elem | Find an element by its non-qualified local name. |
| elems | Get the children elements. |
| get | Get an attribute value by its non-qualified local name. |
| nodeType | Return the XNodeType.elem. |
| prefix | If this element is qualified by an XML namespace then return the namespace's prefix. |
| qname | Qualified name of the element. |
| remove | Remove the child element, text, or PI from this element. |
| removeAt | Remove the child element, text, or PI at the specified index into children. |
| removeAttr | Remove the attribute from this element. |
| removeAttrAt | Remove the attribute at the specified index into attrs. |
| text | Return this element's child text node. |
| toStr | String representation is as a start tag. |
| uri | If this element is qualified by an XML namespace then return the namespace's uri. |
| write | Write this node to the output stream. |
Slot Details
add
@Operator This add(Obj child)
If child is a XAttr then add an attribute. Otherwise it must be a XElem, XText, or XPi and is added a child node. If the child node is already parented, then throw ArgErr. Return this.
addAttr
This addAttr(Str name, Str val, XNs? ns := null)
Add an attribute to this element. Return this. This method is a convenience for:
add(XAttr(name, val, ns))attr
XAttr? attr(Str name, Bool checked := true)
Get an attribute by its non-qualified local name. If the attribute is not found and checked is false then return null otherwise throw XErr.
attrs
XAttr[] attrs()
Get this element's attributes as a readonly list.
children
XNode[] children()
Get this element's children elements, text, and PIs as a readonly list.
clearAttrs
This clearAttrs()
Remove all the attributes. Return this.
copy
This copy()
Make a shallow copy of this element.
each
Void each(|XNode,Int| f)
Iterate each child element, text, and PI node in the children list.
eachAttr
Void eachAttr(|XAttr,Int| f)
Iterate each attribute in the attrs list.
elem
XElem? elem(Str name, Bool checked := true)
Find an element by its non-qualified local name. If there are multiple child elements with the name, then the first one is returned. If the element is not found and checked is false then return null otherwise throw XErr.
elems
XElem[] elems()
Get the children elements. If this element contains text or PI nodes, then they are excluded in the result.
get
@Operator Str? get(Str name, Bool checked := true)
Get an attribute value by its non-qualified local name. If the attribute is not found and checked is false then return null otherwise throw XErr.
line
Int line
Line number of XML element in source file or zero if unknown.
make
new make(Str name, XNs? ns := null)
Construct an element with unqualified local name and optional XML namespace. The XNs instance should be defined as an attribute on this or an ancestor element (see XAttr.makeNs).
name
Str name
Unqualified local name of the element. If an XML namespace prefix was specified, then this is everything after the colon:
<foo> => foo
<x:foo> => foonodeType
virtual override XNodeType nodeType()
Return the XNodeType.elem. Note that during pull parsing XParser will return elemStart and elemEnd.
ns
XNs? ns
The XML namespace which qualified this element's name. If the element name is unqualified return null.
prefix
Str? prefix()
If this element is qualified by an XML namespace then return the namespace's prefix. Otherwise return null. If the namespace is the default namespace then prefix is "".
qname
Str qname()
Qualified name of the element. This is the full name including the XML namespace prefix:
<foo> => foo
<x:foo> => x:fooremove
XNode? remove(XNode child)
Remove the child element, text, or PI from this element. The child is matched by reference, so you must pass in the same XNode contained by this element. Return the removed node or null if no match.
removeAt
XNode removeAt(Int index)
Remove the child element, text, or PI at the specified index into children. Return the removed node.
removeAttr
XAttr? removeAttr(XAttr attr)
Remove the attribute from this element. The attribute is matched by reference, so you must pass in the same XAttr contained by this element. Return the removed attribute or null if no match.
removeAttrAt
XAttr removeAttrAt(Int index)
Remove the attribute at the specified index into attrs. Return the removed attribute.
text
XText? text()
Return this element's child text node. If this element contains multiple text nodes then return the first one. If this element does not contain a text node return null.
toStr
virtual override Str toStr()
String representation is as a start tag.
uri
Uri? uri()
If this element is qualified by an XML namespace then return the namespace's uri. Otherwise return null.
write
virtual override Void write(OutStream out)
Write this node to the output stream.
XErr
@Js const class XErr : Err
XML exception.
Constructors
| make | Construct with optional message, line number, and root cause. |
Fields
| col | Column number of XML error or zero if unknown. |
| line | Line number of XML error or zero if unknown. |
Methods
| toStr | String representation. |
Slot Details
col
const Int col
Column number of XML error or zero if unknown.
line
const Int line
Line number of XML error or zero if unknown.
make
new make(Str? message := null, Int line := 0, Int col := 0, Err? cause := null)
Construct with optional message, line number, and root cause.
toStr
virtual override Str toStr()
String representation.
XIncompleteErr
@Js const class XIncompleteErr : XErr
Incomplete document exception indicates that the end of stream was reached before the end of the document was parsed.
Constructors
| make | Construct with optional message, line number, and root cause. |
Slot Details
make
new make(Str? message := null, Int line := 0, Int col := 0, Err? cause := null)
Construct with optional message, line number, and root cause.
XNode
@Js abstract class XNode : Obj
XNode is the base class for XElem and XText.
Fields
| parent | Get the parent of this node or null if unmounted. |
Methods
| doc | Get the root document node or null if this node is not mounted under a XDoc instance. |
| nodeType | Return the node type enum. |
| write | Write this node to the output stream. |
| writeToStr | Conveniece to write to an in-memory string. |
Slot Details
doc
XDoc? doc()
Get the root document node or null if this node is not mounted under a XDoc instance.
nodeType
abstract XNodeType nodeType()
Return the node type enum. Note that XElem always returns elem, but that during pull parsing XParser will return elemStart and elemEnd.
parent
XNode? parent { internal set }
Get the parent of this node or null if unmounted.
write
abstract Void write(OutStream out)
Write this node to the output stream.
writeToStr
Str writeToStr()
Conveniece to write to an in-memory string.
XNodeType
@Js @Serializable { simple=true } enum class XNodeType : Enum
Enumerates the type of XNode and current node of XParser.
Enum
| doc | Document node type by XDoc.nodeType |
| elem | Element node type returned by XElem.nodeType |
| text | Text node type returned by XText.nodeType |
| pi | Processing instruction node type returned by XPi.nodeType |
| elemStart | Start of element used by XParser when pull parsing. |
| elemEnd | End of element used by XParser when pull parsing. |
Constructors
| fromStr | Return the XNodeType instance for the specified name. |
Fields
| vals | List of XNodeType values indexed by ordinal |
Slot Details
doc
const static XNodeType doc := ...
Document node type by XDoc.nodeType
elem
const static XNodeType elem := ...
Element node type returned by XElem.nodeType
elemEnd
const static XNodeType elemEnd := ...
End of element used by XParser when pull parsing.
elemStart
const static XNodeType elemStart := ...
Start of element used by XParser when pull parsing.
fromStr
static new fromStr(Str name, Bool checked := true)
Return the XNodeType instance for the specified name. If not a valid name and checked is false return null, otherwise throw ParseErr.
pi
const static XNodeType pi := ...
Processing instruction node type returned by XPi.nodeType
text
const static XNodeType text := ...
Text node type returned by XText.nodeType
vals
const static XNodeType[] vals := ...
List of XNodeType values indexed by ordinal
XNs
@Js const class XNs : Obj
Models a XML Namespace uri. It also defines a prefix to use to qualify element and attribute names. XNs instances are passed to the constructor of XElem and XAttr. You can define the namespace attribute via XAttr.makeNs.
Constructors
| make | Construct an XML namespace with the specified prefix and Uri. |
Fields
| prefix | The prefix used to quality element and attribute names with this namespace's uri. |
| uri | The uri which defines a universally unique namespace. |
Methods
| equals | Two namespaces are equal if they have the same uri. |
| hash | Return the uri's hash code. |
| isDefault | Return if this a default namespace which has a prefix of "". |
| toStr | Return the uri as the string representation. |
Slot Details
equals
virtual override Bool equals(Obj? that)
Two namespaces are equal if they have the same uri.
hash
virtual override Int hash()
Return the uri's hash code.
isDefault
Bool isDefault()
Return if this a default namespace which has a prefix of "".
make
new make(Str prefix, Uri uri)
Construct an XML namespace with the specified prefix and Uri. Pass "" for prefix if this is the default XML namespace.
prefix
const Str prefix
The prefix used to quality element and attribute names with this namespace's uri. If this is the default namespace prefix is "".
toStr
virtual override Str toStr()
Return the uri as the string representation.
uri
const Uri uri
The uri which defines a universally unique namespace.
XParser
@Js class XParser : Obj
XParser is a simple, lightweight XML parser. It may be used as a pull parser by iterating through the element and text sections of an XML stream or it may be used to read an entire XML tree into memory as XElems.
Constructors
| make | Construct input stream to read. |
Fields
| col | Current one based column number. |
| depth | Get the depth of the current element with the document. |
| doc | Get the root document node. |
| line | Current one based line number. |
| nodeType | Get the current node type constant which is always the result of the last call to next. |
Methods
| close | Close the underlying input stream. |
| elem | Get the current element if nodeType is elemStart or elemEnd. |
| elemAt | Get the element at the specified depth. |
| next | Advance the parser to the next node and return the node type. |
| parseDoc | Parse the entire document into memory as a tree of XElems and optionally close the underlying input stream. |
| parseElem | Parse the current element entirely into memory as a tree of XElems and optionally close the underlying input stream. |
| pi | if the current node type is pi return the XPi instance otherwise return null. |
| skip | Skip parses all the content until reaching the end tag of the specified depth. |
| text | If the current type is text the XText instance used to store the character data. |
Slot Details
close
Bool close()
Close the underlying input stream. Return true if the stream was closed successfully or false if the stream was closed abnormally.
col
Int col := 1 { private set }
Current one based column number.
depth
Int depth := -1 { private set }
Get the depth of the current element with the document. A depth of zero indicates the root element. A depth of -1 indicates a position before or after the root element.
doc
XDoc doc := XDoc.<ctor>() { private set }
Get the root document node.
elem
XElem? elem()
Get the current element if nodeType is elemStart or elemEnd. If nodeType is text or pi then this is the parent element. After elemEnd this XElem instance is no longer valid and will be reused for further processing. If depth is -1 return null.
elemAt
XElem elemAt(Int depth)
Get the element at the specified depth. Depth must be between 0 and depth inclusively. Calling elemAt(0) will return the root element and elemAt(depth) returns the current element. If depth is invalid IndexErr is thrown.
line
Int line := 1 { private set }
Current one based line number.
make
new make(InStream in)
Construct input stream to read.
next
XNodeType? next()
Advance the parser to the next node and return the node type. Return the current node type:
XNodeType.elemStartXNodeType.elemEndXNodeType.textXNodeType.pi- null indicates end of stream Also see
nodeType.
nodeType
XNodeType? nodeType { private set }
Get the current node type constant which is always the result of the last call to next. Node type will be:
XNodeType.elemStartXNodeType.elemEndXNodeType.textXNodeType.pi- null indicates end of stream
parseDoc
XDoc parseDoc(Bool close := true)
Parse the entire document into memory as a tree of XElems and optionally close the underlying input stream.
parseElem
XElem parseElem(Bool close := true)
Parse the current element entirely into memory as a tree of XElems and optionally close the underlying input stream.
pi
XPi? pi()
if the current node type is pi return the XPi instance otherwise return null.
skip
Void skip(Int toDepth := this.depth)
Skip parses all the content until reaching the end tag of the specified depth. When this method returns, the next call to next will return the node immediately following the end tag.
text
XText? text()
If the current type is text the XText instance used to store the character data. After a call to next this XText instance is no longer valid and will be reused for further processing. If the current type is not text then return null.
XPi
@Js class XPi : XNode
XML processing instruction node.
Constructors
| make | Construct a processing instruction with specified target and val. |
Fields
| target | Target name for the processing instruction. |
| val | String value of processing instruction. |
Methods
| nodeType | Return the XNodeType.pi. |
| toStr | Return string representation of this processing instruction. |
| write | Write this node to the output stream. |
Slot Details
make
new make(Str target, Str val)
Construct a processing instruction with specified target and val.
nodeType
virtual override XNodeType nodeType()
Return the XNodeType.pi.
target
Str target
Target name for the processing instruction. It must be a valid XML name production.
toStr
virtual override Str toStr()
Return string representation of this processing instruction.
val
Str val
String value of processing instruction. This value must not contain the "?>".
write
virtual override Void write(OutStream out)
Write this node to the output stream.
XText
@Js class XText : XNode
XText represents the character data inside an element.
Constructors
| make | Construct a text node with the specified value. |
Fields
| cdata | If true then this text node was read/will be written as a CDATA section. |
| val | Character data for this text node. |
Methods
| copy | Make a copy of this text node. |
| nodeType | Return the XNodeType.text. |
| toStr | Return the string value (truncated if it is long). |
| write | Write this node to the output stream. |
Slot Details
cdata
Bool cdata
If true then this text node was read/will be written as a CDATA section. If set to true, then val must not contain the "]]>" substring.
copy
This copy()
Make a copy of this text node.
make
new make(Str val)
Construct a text node with the specified value.
nodeType
virtual override XNodeType nodeType()
Return the XNodeType.text.
toStr
virtual override Str toStr()
Return the string value (truncated if it is long).
val
Str val
Character data for this text node. If this text is to be written as a CDATA section, then this value must not contain the "]]>" substring.
write
virtual override Void write(OutStream out)
Write this node to the output stream. If this node is set to be written as a CDATA section and the val string contains the "]]>" substring then throw IOErr.