diff options
| author | ruki <[email protected]> | 2025-11-15 20:02:25 +0800 |
|---|---|---|
| committer | ruki <[email protected]> | 2025-11-15 20:02:25 +0800 |
| commit | 88b0fb7c39cd6b867cdde29fd195f5f2c49b62d7 (patch) | |
| tree | 6c94b4d7f5f34e3aed92923c66db52c68ac74ac1 | |
| parent | 9c8f2febdddbe6e1ca4f6e04d4ba0d6f4f0f364f (diff) | |
improve to parse attrs
| -rw-r--r-- | tests/modules/xml/test.lua | 19 | ||||
| -rw-r--r-- | xmake/core/base/xml.lua | 30 |
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 & 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 |
