large_xml

一个用于读取、写入大型 XML 的纯 Dart 库

用法

示例XML

所有示例代码将使用此xml字符串

<?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>

入门

首先,你需要创建一个XmlDocument对象

该对象保存你的xml字符串并动态更新挂载的XmlNode

然后,你可以访问Xml的根xml节点

注意:如果你想缓存xml节点

必须调用node.mount(),将节点挂载到XmlDocument

这样XmlDocument就可以在XML原始字符串更改时动态更新此XmlNode的指针。

当节点不再需要时,你应该调用node.unmount()

import 'package:large_xml/large_xml.dart';

var doc = XmlDocument.fromString(xmlstr);
var root = XmlDocument.root;

查找节点

现在我们有一个root节点引用对象

如果你想获取子节点引用,可以调用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");
}

如果你想获取并行节点引用,可以调用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");
}

如果你想获取祖先节点引用,可以调用node.findAncestor()

就像into()next()一样,它也支持typeselector

并且你可以通过node.parent轻松获取节点的父节点

节点属性

你可以使用以下方法访问节点的属性

  • node.containsAttribute
  • node.getAttribute
  • node.getAttributes
  • node.getAttributeNode
  • node.addAttribute

像这样直接获取属性值

var root = XmlDocument.root;
var person = doc.root.into(type: XmlElementType.start)!;

// directly get attribute's value
// getAttribute(key) you can pass specified or unspecified namespace attribute key,such as "id" or "x:id" or ":id"
// if you pass key like ":id", it will try to find a attribute which named 'id' and has a namespace
String? id = person.getAttribute(":id");
if(id != null){
  // should be attribute r:id
  print(id);
}

如果你想更新属性,请使用node.getAttributeNode,你将获得一个XmlAttribute对象

注意XmlAttribute不应被缓存,它应该在任何写入操作后立即丢弃,所有XmlAttribute将指向错误的位置

如果你确定在持有该对象期间不会进行任何写入操作,那么你可以保留它。

提示attr.setAttribute会自行更新,你仍然可以在setAttribute之后保留它

一些方法支持链式调用,你可以在链式调用中保留对象,否则你应该通过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();

节点缓存

如果你想持有一个节点

这是一个正确的用法示例

//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;

错误的用法

//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;

节点写入

你可以使用以下方法添加、删除和复制节点

  • XmlNode.create
  • node.remove
  • node.copy

所有写入操作都依赖于XmlNodeInstance对象

它具有以下方法

  • inst.pasteBefore
  • inst.pasteAfter
  • inst.pasteInner

这里有一个关于如何创建新节点的示例

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>

你可以将节点复制到XmlNodeInstance对象

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>

GitHub

查看 Github