large_xml
a pure dart library for reading, writing large xml
Usage
Sample XML
all example code will use this xml string
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<!DOCTYPE root>
<root
xmlns:r="http://schemas.openxmlformats.org/package/2006/relationships"
xmlns:x="http://schemas.openxmlformats.org/package/2006/x"
>
<!-- comment here -->
<person age="1" r:id="2" x:id="3" ></person>
<!-- comment here -->
<x:script>
<![CDATA[function matchwo(a,b){if (a < b && a < 0) then{return 1;}else{return 0;}}]]>
</x:script>
<info>
<child x:name="child name"/>
</info>
</root>
Getting Started
first, you need create a XmlDocument Object
this object holding your xml string and dynamicly update mounted XmlNode
then, you can access Xml's root xml node
NOTICE: if you want to cache the xml node
you MUST invoke node.mount()
, mount the node to XmlDocument
so that XmlDocument
can dynamicly update this XmlNode
's pointer when the XML raw string changing.
you should invoke node.unmount()
when the node is unnecessary
import 'package:large_xml/large_xml.dart';
var doc = XmlDocument.fromString(xmlstr);
var root = XmlDocument.root;
Finding Node
now we have a root
node referece object
if you want to get the child node referece, you can invoke node.into()
var root = XmlDocument.root;
// find first normal start element in root chilren
// return <person/> node
var person = doc.root.into(type: XmlElementType.start);
if(person == null){
print("node not found");
}
// find first normal start element in root chilren and node name is "info"
// return <info/> node
var info = doc.root.into(selector: (n)=>n.type == XmlElementType.start && n.name == "info");
if(info == null){
print("node not found");
}
if you want to get the parallel node referece, you can invoke node.next()
var root = XmlDocument.root;
var person = doc.root.into(type: XmlElementType.start)!;
// find first normal start element after person node
// return <x:script/> node
var script = person.next(type: XmlElementType.start);
if(script == null){
print("node not found");
}
// find first normal start element in root chilren and node name is "info"
// return <info/> node
var info = doc.root.next(selector: (n)=>n.type == XmlElementType.start && n.name == "info");
if(info == null){
print("node not found");
}
// find first normal start element after person node and node name is "script", ignore namespace
// return <x:script/> node
script = doc.root.next(selector: (n)=>n.type == XmlElementType.start && n.name.removeNamespace() == "script");
if(script == null){
print("node not found");
}
// find first normal start element after person node and namespace is "x"
// return <x:script/> node
script = doc.root.next(selector: (n)=>n.type == XmlElementType.start && n.name.namespace() == "x");
if(script == null){
print("node not found");
}
// find first normal start element after person node and specified whole match "x:script"
// return <x:script/> node
script = doc.root.next(selector: (n)=>n.type == XmlElementType.start && n.name == "x:script");
if(script == null){
print("node not found");
}
if you want to get the ancestor node referece, you can invoke node.findAncestor()
like into()
and next()
, it also support type
and selector
and you can easily get node's parent node by node.parent
Node Attribute
you can use following method to access node's attribute
node.containsAttribute
node.getAttribute
node.getAttributes
node.getAttributeNode
node.addAttribute
directly get attribute's value like this
var root = XmlDocument.root;
var person = doc.root.into(type: XmlElementType.start)!;
// directly get attribute's value
// parameter key should like these
// "id" or "x:id" or ":id" or "*id"
// - "id" no namespace named "id" attribute
// - "x:id" namespace is "x" and name is "id"
// - ":id" has a namespace and name is "id"
// - "*id" just name is "id"
String? id = person.getAttribute(":id");
if(id != null){
// should be attribute r:id
print(id);
}
if you want to update attribute, use node.getAttributeNode
, you will get a XmlAttribute
object
NOTICE: XmlAttribute
should not be cached
it should be dropped immediately
after any writing operation, all XmlAttribute
will point at wrong position
if you make sure that there will be no write action while holding this object, then you can keep it.
tips:attr.setAttribute
will update it self, you can still keep it after setAttribute
some method support chain invoke, you can keep the object in a chain
otherwise you should re-find it by node.getAttributeNode
.
var root = XmlDocument.root;
var person = doc.root.into(type: XmlElementType.start)!;
XmlAttribute id = person.getAttributeNode(":id")!;
print(id.key); // x:id
// id.namepace should be http://schemas.openxmlformats.org/package/2006/relationships
// it will lookup it ancestors and find first matched "xmlns:r" definition
print(id.namespace);
print(value); // 2
id.setAttribute("new value");
print(value); // new value
id.remove();
person.addAttribute("nattr")!;
print(person.containsAttribute("nattr")); // true
// you can get attributes map like this
Map<String,String> attrs = person.getAttributes();
Node Cache
if you want to holding a node
this is a correct usage example
//root is defaultly mounted
var root = XmlDocument.root;
var person = doc.root
.into(type: XmlElementType.start)!
.mount(); // mounted
var info = doc.root
.next(selector: (n)=>n.type == XmlElementType.start && n.name == "info")!
.mount(); // mounted
person.addAttribute("nattr");
info.addAttribute("nattr");
person.unmount();// release
info.unmount();// release
return;
wrong usage
//root is defaultly mounted
var root = XmlDocument.root;
var person = doc.root
.into(type: XmlElementType.start)!
.mount(); // mounted
var info = doc.root
.next(selector: (n)=>n.type == XmlElementType.start && n.name == "info")!; // unmounted
person.addAttribute("nattr");
// after person node write action
// info node still keep the origin pointer, so it will write the attribute on wrong position
info.addAttribute("nattr");
person.unmount();// release
return;
Node Write
you can use following method to add, remove, and copy a node
XmlNode.create
node.remove
node.copy
and all writing action is depending on XmlNodeInstance
object
it has following method:
inst.pasteBefore
inst.pasteAfter
inst.pasteInner
here is a example about how to create a new node
var root = XmlDocument.root;
var person = doc.root
.into(type: XmlElementType.start)!
.mount();
XmlNodeInstance inst = XmlNode.create("new");
var newNode = inst.pasteBefore(person).mount();
// xml will be changed like this
// <root>
// <new/> <-- new node will be add before person node
// <person/>
// ...
// </root>
you can copy a node to a XmlNodeInstance
object
var root = XmlDocument.root;
var person = doc.root
.into(type: XmlElementType.start)!
.mount();
var info = doc.root
.next(selector: (n)=>n.type == XmlElementType.start && n.name == "info")!
.mount();
XmlNodeInstance copy = info.copy();
var newNode = inst.pasteInner(person).mount();
// xml will be changed like this
// <root>
// <person>
// <info> <-- copy to here
// <child/>
// </info>
// </person>
// ...
// </root>