When the value of the map is of type java.time.Duration(According to the source code, it seems that this problem exists for non Java.lang class types), it will not be mapped correctly if "[]" is not used on the key which has the dot in the yaml file. Just like this:

leo:
  yyy:
    expiry: #Map<String, Duration>
      "[http.server.requests]": 5ms   # it's fine
      http.server.request: 5ms    # don't work
    integer-test:
      "[http.server.requests]": 3   # it's fine
      http.server.request: 4    # it's fine

If the value type in map is Integer or Boolean or other types of java.lang, their keys can be mapped correctly without brackets. If it is confirmed that it is a bug and can make relevant suggestions, I will be very happy to fix it. Here is the test case: Case

Comment From: snicoll

Please review the documentation before reporting an issue, there is an explicit section about it.

Comment From: wilkinsona

This doesn't feel quite right to me. I would argue that Duration is a scalar data type, so, as currently written, the documentation implies to me that there should be no need for bracket notation. I think it'd be nice if the runtime's behaviour matched the documentation as there doesn't appear to be any ambiguity that could result in a nested map being created.

Here's a complete example:

package com.example.demo;

import java.time.Duration;
import java.util.Map;

import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;

@SpringBootApplication
@EnableConfigurationProperties(ExampleProperties.class)
public class Gh27581Application {

    public Gh27581Application(ExampleProperties properties) {
        System.out.println(properties.getAlpha());
        System.out.println(properties.getBravo());
    }

    public static void main(String[] args) {
        new SpringApplicationBuilder(Gh27581Application.class)
            .properties(
                "example.alpha.one:1s",
                "example.alpha.two.three:23s",
                "example.alpha.[four.five]:45s",
                "example.bravo.one:1",
                "example.bravo.two.three:23",
                "example.bravo.[four.five]:45")
            .run(args);
    }

}

@ConfigurationProperties("example")
class ExampleProperties {

    private Map<String, Duration> alpha;

    private Map<String, Integer> bravo;

    public Map<String, Duration> getAlpha() {
        return alpha;
    }

    public void setAlpha(Map<String, Duration> alpha) {
        this.alpha = alpha;
    }

    public Map<String, Integer> getBravo() {
        return bravo;
    }

    public void setBravo(Map<String, Integer> bravo) {
        this.bravo = bravo;
    }

}

example.alpha.two.three:23s is silently ignored:

  .   ____          _            __ _ _
 /\\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \\/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::                (v2.5.3)

2021-08-09 13:50:47.629  INFO 14581 --- [           main] com.example.demo.Gh27581Application      : Starting Gh27581Application using Java 11.0.10 on wilkinsona-a01.vmware.com with PID 14581 (/Users/awilkinson/dev/workspaces/spring-projects/spring-boot/main/gh-27581/target/classes started by awilkinson in /Users/awilkinson/dev/workspaces/spring-projects/spring-boot/main/gh-27581)
2021-08-09 13:50:47.630  INFO 14581 --- [           main] com.example.demo.Gh27581Application      : No active profile set, falling back to default profiles: default
{four.five=PT45S, one=PT1S}
{one=1, four.five=45, two.three=23}
2021-08-09 13:50:47.984  INFO 14581 --- [           main] com.example.demo.Gh27581Application      : Started Gh27581Application in 0.632 seconds (JVM running for 0.903)

Comment From: polarbear567

@wilkinsona means exactly what I want to say.

Comment From: wilkinsona

example.alpha.two.three:23s is dropped because Duration doesn't have a default constructor. This means that JavaBeanBinder cannot create a Bean for it so bind returns null:

https://github.com/spring-projects/spring-boot/blob/9822cad51053c5886c27ac025c42f1a3580b470f/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/JavaBeanBinder.java#L50-L61

Things then unwind gracefully from here.

When handling the null bind result, no attempt is made to create a Duration as create is false when binding an aggregate:

https://github.com/spring-projects/spring-boot/blob/9822cad51053c5886c27ac025c42f1a3580b470f/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/context/properties/bind/Binder.java#L428-L436

Comment From: wilkinsona

With ignoreUnknownFields set to false example.alpha.two.three=23s causes a failure:

***************************
APPLICATION FAILED TO START
***************************

Description:

Binding to target [Bindable@74518890 type = com.example.demo.ExampleProperties, value = 'provided', annotations = array<Annotation>[@org.springframework.boot.context.properties.ConfigurationProperties(ignoreInvalidFields=false, ignoreUnknownFields=false, prefix=example, value=example)]] failed:

    Property: example.alpha.two.three
    Value: 23s
    Origin: "example.alpha.two.three" from property source "defaultProperties"
    Reason: The elements [example.alpha.two.three] were left unbound.

Action:

Update your application's configuration

This doesn't feel quite right to me as the field isn't really unknown. It feels like it should always fail.

Comment From: philwebb

I've updated this one to a documentation issue. We'll improve this section to list the scalar types we support.

I've also created and linked a few related issues (#14796, #27695 and #14796).