spring-framework icon indicating copy to clipboard operation
spring-framework copied to clipboard

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

Open ian4hu opened this issue 4 years ago • 3 comments

Affects: since 4.1


Yaml file like bellow:

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

Yaml's map to json like

{
   "webservices": {
      "[domain.test:8080]": [
         {
            "username": "me",
            "password": "mypassword"
         }
      ]
   }
}

should equals properties file like:

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

or:

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

Both properties file is acceptable for Spring Boot Binding Maps

Reproduce Test Case is

	@Test
	void loadNestedEscapedProperties() throws Exception {

		String yaml = "root:\n" +
				"  webservices:\n" +
				"    \"[domain.test:8080]\":\n" +
				"      - username: me\n" +
				"        password: mypassword\n";
		String propsStr = "root.webservices[[domain.test\\:8080]][0].username=me\n" +
				"root.webservices[[domain.test\\:8080]][0].password=mypassword";
		final PropertiesFactoryBean factory = new PropertiesFactoryBean();
		factory.setLocations(new ByteArrayResource(propsStr.getBytes(StandardCharsets.UTF_8)));
		factory.setSingleton(false);
		final Properties properties = factory.getObject();

		final YamlPropertiesFactoryBean yamlFactory = new YamlPropertiesFactoryBean();
		yamlFactory.setResources(new ByteArrayResource(yaml.getBytes(StandardCharsets.UTF_8)));
		assertThat(yamlFactory.getObject()).isEqualTo(properties);
	}

Current it is failed like bellow

expected: {"root.webservices[[domain.test:8080]][0].password"="mypassword", "root.webservices[[domain.test:8080]][0].username"="me"}
but was : {"root.webservices[domain.test:8080][0].password"="mypassword", "root.webservices[domain.test:8080][0].username"="me"}
org.opentest4j.AssertionFailedError: 
expected: {"root.webservices[[domain.test:8080]][0].password"="mypassword", "root.webservices[[domain.test:8080]][0].username"="me"}
but was : {"root.webservices[domain.test:8080][0].password"="mypassword", "root.webservices[domain.test:8080][0].username"="me"}
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at org.springframework.beans.factory.config.YamlPropertiesFactoryBeanTests.loadNestedEscapedProperties(YamlPropertiesFactoryBeanTests.java:270)

It seems there is a bug cause yaml's nested map is flatten incorrectly like

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

ian4hu avatar Jun 04 '21 03:06 ian4hu

See https://github.com/spring-cloud/spring-cloud-config/issues/1595

ian4hu avatar Jun 04 '21 07:06 ian4hu

related #27022

ian4hu avatar Jun 04 '21 07:06 ian4hu

@ian4hu you said you had some code to attempt to fix this issue. Would you mind sharing that?

snicoll avatar Sep 27 '23 14:09 snicoll