summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorruki <[email protected]>2025-11-15 20:02:25 +0800
committerruki <[email protected]>2025-11-15 20:02:25 +0800
commit88b0fb7c39cd6b867cdde29fd195f5f2c49b62d7 (patch)
tree6c94b4d7f5f34e3aed92923c66db52c68ac74ac1
parent9c8f2febdddbe6e1ca4f6e04d4ba0d6f4f0f364f (diff)
improve to parse attrs
-rw-r--r--tests/modules/xml/test.lua19
-rw-r--r--xmake/core/base/xml.lua30
2 files changed, 47 insertions, 2 deletions
diff --git a/tests/modules/xml/test.lua b/tests/modules/xml/test.lua
index 985600a9d..41c56c867 100644
--- a/tests/modules/xml/test.lua
+++ b/tests/modules/xml/test.lua
@@ -12,6 +12,25 @@ function test_decode_basic(t)
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 &amp; 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",
diff --git a/xmake/core/base/xml.lua b/xmake/core/base/xml.lua
index beb3a2ed4..7dc0025b8 100644
--- a/xmake/core/base/xml.lua
+++ b/xmake/core/base/xml.lua
@@ -84,10 +84,36 @@ end
-- parse attribute string to table (or nil if empty)
function xml._parse_attrs(attrstr)
local attrs
- attrstr:gsub("([%w_:%-%.]+)%s*=%s*([\"'])(.-)%2", function(key, quote, value)
+ local i, len = 1, #attrstr
+ while i <= len do
+ local key, value_start = attrstr:match("^%s*([%w_:%-%.]+)%s*=%s*()", i)
+ if not key then
+ break
+ end
+ local value
+ local first_char = attrstr:sub(value_start, value_start)
+ if first_char == "\"" or first_char == "'" then
+ local closing = attrstr:find(first_char, value_start + 1, true)
+ if not closing then
+ break
+ end
+ value = attrstr:sub(value_start + 1, closing - 1)
+ i = closing + 1
+ else
+ local j = value_start
+ while j <= len do
+ local ch = attrstr:sub(j, j)
+ if ch:match("%s") or ch == ">" or (ch == "/" and attrstr:sub(j + 1, j + 1) == ">") then
+ break
+ end
+ j = j + 1
+ end
+ value = attrstr:sub(value_start, j - 1)
+ i = j
+ end
attrs = attrs or {}
attrs[key] = xml._decode_entities(value)
- end)
+ end
return attrs
end