Skip to content

ixml: escape and unescape attribute values - #1228

Merged
larshp merged 1 commit into
open-abap:mainfrom
abap2UI5:claude/nifty-meitner-ema3y8-attribute-unescape
Sep 15, 2026
Merged

larshp merged 1 commit into
open-abap:mainfrom
abap2UI5:claude/nifty-meitner-ema3y8-attribute-unescape

Conversation

@oblomov-dev

Copy link
Copy Markdown
Contributor

What is wrong

An attribute value passes both ends of cl_ixml untouched.

Reading, parse_attributes sets the submatch of the regex as the value:

CREATE OBJECT li_node TYPE lcl_node.
li_node->set_name( lv_name ).
li_node->set_value( lv_value ).

so <item foo="a&lt;b"/> reads as the seven characters a&lt;b, where a
system gives a<b. The character data of an element is unescaped one method
away (lcl_escape=>unescape_value); an attribute never was.

Writing, if_ixml_element~render concatenates the value as it is:

lv_attributes = lv_attributes && | | && lv_ns && li_node->get_name( ) && '="' && li_node->get_value( ) && '"'.

so an attribute holding a<b renders as foo="a<b" and one holding a quote
ends the attribute early — the document is not well-formed, and the parser
above reads it back as something else or not at all.

The change

The value goes through lcl_escape on both ends, the same routines the
character data of an element already uses:

li_node->set_value( lcl_escape=>unescape_value( lv_value ) ).
lv_attributes = lv_attributes && | | && lv_ns && li_node->get_name( ) &&
  '="' && lcl_escape=>escape_value( li_node->get_value( ) ) && '"'.

Two lines. escape_value writes all five named entities, the apostrophe
included — inside a double-quoted attribute &apos; is not the shortest form
but it is correct, and it keeps the routine the one both sides share.

Tests

  • parse_escaped_attributefoo="a&lt;b&amp;c" reads as a<b&c
  • render_escaped_attribute — a value of &<>" renders as
    &amp;&lt;&gt;&quot;
  • attribute_roundtrip — a document with an escaped attribute comes out of
    parse and render exactly as it went in

npm test is green: abaplint: 0 issue(s) found, 735 file(s) analyzed, and
the full unit run passes.

Relation to the other ixml PRs

Independent of the character-reference change — that one teaches
unescape_value the &#10; and &#x41; forms. This one is what routes an
attribute through unescape_value at all, so with both merged a character
reference in an attribute resolves too. Either can go first.

Both ends of an attribute value went through untouched: `parse_attributes`
set the submatch of the regex as the value, so `<item foo="a&lt;b"/>` read as
`a&lt;b`, and `if_ixml_element~render` wrote the value raw, so an attribute
holding `a<b` or a quote produced a document that is not well-formed.

The value now goes through lcl_escape on both ends, the same routines the
character data of an element already uses - so what the renderer writes the
parser reads back as the same value.

Tests: an escaped attribute read back unescaped, the renderer escaping the
five characters, and a document that survives parse and render unchanged.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TKSWLpid4QLV6XWAoPixbb
@oblomov-dev
oblomov-dev force-pushed the claude/nifty-meitner-ema3y8-attribute-unescape branch from e3fe41a to add23f1 Compare September 15, 2026 11:07
@larshp
larshp merged commit f9c26ec into open-abap:main Sep 15, 2026
7 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants