1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
|
import("core.base.xml")
function test_decode_basic(t)
local doc = xml.decode([[<?xml version="1.0"?><root id="1"><item>foo</item><item id="2"/></root>]])
t:are_equal(doc.kind, "element")
t:are_equal(doc.name, "root")
t:are_equal(doc.attrs.id, "1")
t:are_equal(#doc.children, 2)
t:are_equal(doc.children[1].name, "item")
t:are_equal(xml.text_of(doc.children[1]), "foo")
t:are_equal(doc.children[1].attrs, nil)
t:are_equal(doc.children[2].attrs.id, "2")
end
function test_decode_unquoted_attrs(t)
local doc = xml.decode("<root flag=true count=42 path=/tmp/file><child data=abc/></root>")
t:are_equal(doc.attrs.flag, "true")
t:are_equal(doc.attrs.count, "42")
t:are_equal(doc.attrs.path, "/tmp/file")
t:are_equal(doc.children[1].attrs.data, "abc")
end
function test_decode_mixed_attrs(t)
local xmltext = [[<node a="1 2" b='foo & bar' c=bare data-id=abc123 ns:flag=true dashed-name="hello-world"/>]]
local doc = xml.decode(xmltext)
t:are_equal(doc.attrs.a, "1 2")
t:are_equal(doc.attrs.b, "foo & bar")
t:are_equal(doc.attrs.c, "bare")
t:are_equal(doc.attrs["data-id"], "abc123")
t:are_equal(doc.attrs["ns:flag"], "true")
t:are_equal(doc.attrs["dashed-name"], "hello-world")
end
function test_encode_basic(t)
local doc = xml.new({
name = "root",
attrs = {id = "1"},
children = {
xml.new({name = "item", children = {xml.text("foo")}}),
xml.new({name = "item", attrs = {id = "2"}})
}
})
local compact = xml.encode(doc)
t:are_equal(compact, '<root id="1"><item>foo</item><item id="2"/></root>')
local pretty = xml.encode(doc, {pretty = true, indent = 2})
local expected = table.concat({
'<root id="1">',
' <item>foo</item>',
' <item id="2"/>',
'</root>'
}, "\n")
t:are_equal(pretty, expected)
end
function test_encode_special_nodes(t)
t:are_equal(xml.encode(xml.comment("note")), "<!--note-->")
t:are_equal(xml.encode(xml.cdata("a < b")), "<![CDATA[a < b]]>")
t:are_equal(xml.encode(xml.doctype("note SYSTEM \"note.dtd\"")), "<!DOCTYPE note SYSTEM \"note.dtd\">")
t:are_equal(xml.encode(xml.empty("br")), "<br/>")
end
function test_decode_special_nodes(t)
local doc = xml.decode([=[<root><!--note--><![CDATA[a < b]]><child/></root>]=])
t:are_equal(doc.children[1].kind, "comment")
t:are_equal(doc.children[1].text, "note")
t:are_equal(doc.children[2].kind, "cdata")
t:are_equal(doc.children[2].text, "a < b")
t:are_equal(doc.children[3].name, "child")
local nodes = xml.decode([=[<!DOCTYPE note SYSTEM "note.dtd"><root/>]=])
t:are_equal(nodes.kind, "element")
t:are_equal(nodes.name, "root")
t:are_equal(nodes.prolog[1].kind, "doctype")
end
function test_load_save(t)
local tmpdir = os.tmpdir()
local filepath = path.join(tmpdir, "xml_test.xml")
local doc = xml.new({
name = "root",
attrs = {id = "1"},
children = {xml.text("hello")}
})
assert(xml.savefile(filepath, doc, {pretty = true}))
local reloaded = xml.loadfile(filepath)
t:are_equal(reloaded.name, "root")
t:are_equal(xml.text_of(reloaded), "hello")
os.tryrm(filepath)
end
function test_plist_sample(t)
local plist = [[<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
<key>NSHumanReadableCopyright</key>
<string>Copyright © 2020 tboox. All rights reserved.</string>
<key>NSSupportsAutomaticTermination</key>
<true/>
</dict>
</plist>]]
local doc = xml.decode(plist)
t:are_equal(doc.name, "plist")
t:are_equal(doc.attrs.version, "1.0")
t:are_equal(doc.prolog[1].kind, "doctype")
local dict = xml.find(doc, "plist/dict")
t:are_equal(dict.kind, "element")
local first_key = dict.children[1]
t:are_equal(first_key.name, "key")
t:are_equal(xml.text_of(first_key), "CFBundleDevelopmentRegion")
local first_value = dict.children[2]
t:are_equal(first_value.name, "string")
t:are_equal(xml.text_of(first_value), "$(DEVELOPMENT_LANGUAGE)")
local last_flag = dict.children[#dict.children]
t:are_equal(last_flag.name, "true")
end
function test_scan_stop(t)
local plist = [[
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
</dict>
</plist>]]
local found
xml.scan(plist, function(node)
if node.name == "key" and xml.text_of(node) == "NSPrincipalClass" then
found = node
return false
end
end)
t:are_equal(found ~= nil, true)
t:are_equal(xml.text_of(found), "NSPrincipalClass")
end
function test_find_xpath(t)
local doc = xml.decode([[
<root>
<items>
<item id="a"><value>foo</value></item>
<item id="b"><value>bar</value></item>
</items>
<extras>
<item id="c"/>
</extras>
</root>]])
local second = xml.find(doc, "root/items/item[2]")
t:are_equal(second.attrs.id, "b")
local descendant = xml.find(doc, "//item[@id='c']")
t:are_equal(descendant.attrs.id, "c")
local value = xml.find(doc, "//value[text()='bar']")
t:are_equal(xml.text_of(value), "bar")
end
function test_find_update(t)
local doc = xml.decode("<root><item id='a'>foo</item><item id='b'/></root>")
local target = xml.find(doc, "//item[@id='a']")
t:are_not_equal(target, nil)
target.attrs.lang = "en"
target.children = {xml.text("bar")}
local new_item = xml.new({name = "item", attrs = {id = "c"}, children = {xml.text("baz")}})
table.insert(doc.children, new_item)
local encoded = xml.encode(doc)
t:are_equal(encoded, '<root><item id="a" lang="en">bar</item><item id="b"/><item id="c">baz</item></root>')
end
function test_decode_trim_text(t)
local doc = xml.decode("<root> foo </root>")
t:are_equal(xml.text_of(doc), " foo ")
local trimmed = xml.decode("<root> foo </root>", {trim_text = true})
t:are_equal(xml.text_of(trimmed), "foo")
local formatted = "<root>\n <item/>\n</root>"
local default = xml.decode(formatted)
t:are_equal(#default.children, 1)
local keep_ws = xml.decode(formatted, {keep_whitespace_nodes = true})
t:are_equal(#keep_ws.children, 3)
t:are_equal(keep_ws.children[1].kind, "text")
end
|