Skip to content

Preserve user-supplied bracket map keys when flattening YAML#36724

Open
daguimu wants to merge 1 commit intospring-projects:mainfrom
daguimu:fix/yaml-bracket-key-flattening-27020
Open

Preserve user-supplied bracket map keys when flattening YAML#36724
daguimu wants to merge 1 commit intospring-projects:mainfrom
daguimu:fix/yaml-bracket-key-flattening-27020

Conversation

@daguimu
Copy link
Copy Markdown
Contributor

@daguimu daguimu commented Apr 29, 2026

Problem

YamlPropertiesFactoryBean (and any other YamlProcessor consumer) drops the literal brackets on user-supplied YAML map keys whose textual form starts with [. For example:

root:
  webservices:
    "[domain.test:8080]":
      - username: me
        password: mypassword

today flattens to:

root.webservices[domain.test:8080][0].username=me

so a downstream consumer such as Spring Boot's relaxed binder sees the map key as domain.test:8080, losing the user-typed brackets entirely.

Root Cause

YamlProcessor#buildFlattenedMap (spring-beans/.../YamlProcessor.java) treats every key starting with [ the same way:

if (key.startsWith("[")) {
    key = path + key;          // no dot separator
}

That branch is intended for internally-generated bracket keys – collection indices ([0], [1], …) and the toString() of non-CharSequence map keys (asMap line 254 wraps them as [<toString>]). When a user-supplied YAML key happens to start with [ it is silently misclassified as an internal index marker and the brackets are stripped from the resulting property path.

Fix

Distinguish the two cases by inspecting characters that never appear in internally-generated bracket keys:

  • collection indices are always [<digits>]
  • toString() of common non-CharSequence keys (Integer, Long, Boolean, …) contains no . or :

So, inside the key.startsWith("[") branch, when the key contains . or : it must have been written by the user. In that case the key is wrapped in an extra pair of brackets so the original brackets become part of the property path:

root.webservices[[domain.test:8080]][0].username=me

asMap and the value-tree returned to YamlMapFactoryBean users are unchanged – the additional wrapping happens only inside buildFlattenedMap, on the flatten path.

Tests Added

Change point Test
Bracket-prefixed user key with . and : (the exact reproduction from the issue) is wrapped loadNestedMapWithBracketedKeyContainingDotAndColon()
Bracket-prefixed user key with . only is wrapped loadNestedMapWithBracketedKeyContainingDot()
Bracket-prefixed user key with : only is wrapped loadNestedMapWithBracketedKeyContainingColon()
Internal collection-index keys ([0], [1], …) and Integer map keys ([1]) are unchanged existing loadArrayOf* and integerKeyBehaves / integerDeepKeyBehaves tests still pass

All spring-beans YAML tests (YamlPropertiesFactoryBeanTests, YamlMapFactoryBeanTests, YamlProcessorTests – 43 tests) pass locally.

Impact

  • YamlPropertiesFactoryBean / any YamlProcessor consumer that calls getFlattenedMap: bracket-prefixed YAML keys with . or : are now preserved with an additional bracket pair, matching what the Spring Boot binder expects for map keys with special characters.
  • YamlMapFactoryBean (tree path) and existing internal bracket keys: unchanged.

Closes gh-27020

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Apr 29, 2026
YamlProcessor#buildFlattenedMap treated every key starting with '[' as an
internal bracket-wrapped key (collection indices and toString() of
non-CharSequence map keys) and concatenated it to the parent path
without a dot. This silently dropped the literal brackets on
user-supplied keys like the YAML literal '[domain.test:8080]'.

Distinguish the two by checking whether the bracket-prefixed key
contains characters never produced for internal keys (currently '.'
and ':'). When such a separator is present the key is treated as a
user-supplied map key whose original brackets are wrapped again so
that downstream consumers (e.g. the Spring Boot binder) see the
brackets as part of the key.

Closes spring-projectsgh-27020

Signed-off-by: daguimu <daguimu.geek@gmail.com>
@daguimu daguimu force-pushed the fix/yaml-bracket-key-flattening-27020 branch from 95ab4dd to ece2940 Compare April 29, 2026 08:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

status: waiting-for-triage An issue we've not yet triaged or decided on

Projects

None yet

Development

Successfully merging this pull request may close these issues.

YamlPropertiesFactoryBean incorrect flatten nested map to properties when map key contains escaped brackets

2 participants