lightbend / config

configuration library for JVM languages using HOCON files
https://lightbend.github.io/config/
6.16k stars 967 forks source link

`ConfigFactory.parseResources()` overrides `ConfigParseOptions.setSyntax()` #772

Open brandoncimino opened 2 years ago

brandoncimino commented 2 years ago

If Java's URLConnection.getContentType() returns one of the application/json, application/hocon, or text/x-java-properties, then that value will override the provided ConfigParseOptions.syntax:

https://github.com/lightbend/config/blob/4458ea947a7a2a668bb811a122455f1f05975172/config/src/main/java/com/typesafe/config/impl/Parseable.java#L236-L244

This can cause errors based on the current Java version, because at some point between Java 11 and Java 18, the content-type of a local .json file started coming back as application/json instead of content/unknown:

Example

static void parseConfigWithSyntax(Path path) {
    System.out.println("Java version:  " + System.getProperty("java.version"))
    System.out.println("path:          " + path);
    String contentType = path
            .toUri()
            .toURL()
            .openConnection()
            .getContentType();
    System.out.println("content type:  " + contentType);

    def options = ConfigParseOptions.defaults().setSyntax(ConfigSyntax.CONF);
    System.out.println("parse syntax:  " + options.getSyntax());

    try {
        System.out.println("\n== via ConfigFactory.parseFile(" + path + ") ==")
        var fromFile = ConfigFactory.parseFile(path.toFile(), options);
        System.out.println(fromFile.root().render());
    } catch (Exception e) {
        System.out.println(e);
    }

    try {
        System.out.println("\n== via ConfigFactory.parseResources(" + path.getFileName() + ") ==");
        var fromResource = ConfigFactory.parseResources(path.getFileName().toString(), options);
        System.out.println(fromResource.root().render());
    } catch (Exception e) {
        System.out.println(e);
    }
}

Java 11

Java version:  11.0.10
path:          C:\Users\bcimino\dev\tqa\target\classes\has_comments.json
content type:  content/unknown
parse syntax:  CONF

== via ConfigFactory.parseFile(C:\Users\bcimino\dev\tqa\target\classes\has_comments.json) ==
{
    # C:\Users\bcimino\dev\tqa\target\classes\has_comments.json: 3
    # this is a comment
    "a" : 5
}

== via ConfigFactory.parseResources(has_comments.json) ==
{
    # has_comments.json @ file:/C:/Users/bcimino/dev/tqa/target/classes/has_comments.json: 3
    # this is a comment
    "a" : 5
}

Java 18

Java version:  18.0.1
path:          C:\Users\bcimino\dev\tqa\target\classes\has_comments.json
content type:  application/json
parse syntax:  CONF

== via ConfigFactory.parseFile(C:\Users\bcimino\dev\tqa\target\classes\has_comments.json) ==
{
    # C:\Users\bcimino\dev\tqa\target\classes\has_comments.json: 3
    # this is a comment
    "a" : 5
}

== via ConfigFactory.parseResources(has_comments.json) ==
com.typesafe.config.ConfigException$Parse: has_comments.json @ file:/C:/Users/bcimino/dev/tqa/target/classes/has_comments.json: 2: Token not allowed in valid JSON: '//'

Workaround

This can be worked around by loading the file "by hand" into a String and using ConfigFactory.parseString().