Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parse yaml to List<POJO> #391

Closed
martinvisser opened this issue Sep 10, 2020 · 3 comments
Closed

Parse yaml to List<POJO> #391

martinvisser opened this issue Sep 10, 2020 · 3 comments

Comments

@martinvisser
Copy link

Since an upgrade to Quarkus 1.6.1 and thereby smallrye 1.8.4 the application's yaml is parsed differently. Upgrading to Quarkus 1.7.3 will update to smallrye 18.5, but that gives the same result. I find it impossible to get to the same result as with smallrye 1.8.1.
Any hints on how to parse this yaml into a List of POJOs?

---
app:
  comics:
    - name: comic1
      uri: http://example1.com
      pattern: '<some-example1/>'
    - name: comic2
      uri: http://example2.com
      pattern: '<some-example2/>'
    - name: comic3
      uri: http://example3.com
      pattern: '<some-example3/>'

The POJO:

import io.quarkus.arc.config.ConfigProperties;
import java.net.URI;
import java.util.List;
import java.util.regex.Pattern;

@ConfigProperties(prefix = "app")
public class ComicProperties {
    public List<Comic> comics;

    public static class Comic {
        public String name;
        public URI uri;
        public Pattern pattern;

        public Comic(final String name, final String uri, final String pattern) {
            this.name = name;
            this.uri = URI.create(uri);
            this.pattern = Pattern.compile(pattern);
        }
    }
}

The converter (not so relevant):

import org.eclipse.microprofile.config.spi.Converter;
import java.util.regex.Pattern;

public class ComicPropertiesConverter implements Converter<ComicProperties.Comic> {
    private static final Pattern PROPERTY_PATTERN = Pattern.compile("name=(.+)uri=(.+)pattern=(.+)");

    @Override
    public ComicProperties.Comic convert(final String value) {
        final var matcher = PROPERTY_PATTERN.matcher(value);
        if (matcher.find()) {
            return new ComicProperties.Comic(matcher.group(1), matcher.group(2), matcher.group(3));
        }
        throw new IllegalArgumentException("Cannot convert " + value);
    }
}

In smallrye 1.8.1 this results in one property:

"app.comics" -> "name\=comic1uri\=http://example1.compattern\=<some-example1/>,name\=comic2uri\=http://example2.compattern\=<some-example2/>,name\=comic3uri\=http://example3.compattern\=<some-example3/>"

This combined with the converter I could translate into the wanted Comic. Had to parse it with a regex, but it worked.

Since 1.8.4 this results in more properties:

"app.comics" -> "{"comics": [{"name": "comic1", "uri": "http://example1.com", "pattern": "<some-example1/>"},\n    {"name": "comic2", "uri": "http://example2.com", "pattern": "<some-example2/>"},\n    {"name": "comic3", "uri": "http://example3.com", "pattern": "<some-example3/>"}]}\n"
"app.comics.[0].name" -> "comic1"
"app.comics.[0].pattern" -> "<some-example1/>"
"app.comics.[0].uri" -> "http://example1.com"
"app.comics.[1].name" -> "comic2"
"app.comics.[1].pattern" -> "<some-example2/>"
"app.comics.[1].uri" -> "http://example2.com"
"app.comics.[2].name" -> "comic3"
"app.comics.[2].pattern" -> "<some-example3/>"
"app.comics.[2].uri" -> "http://example3.com"

If I use the same converter here, each "comic" is a part of "app.comics" after a split on a comma. So the first "comic" becomes {"comics": [{"name": "comic1" which obviously doesn't make sense.

@radcortez
Copy link
Member

Hi @martinvisser

We had to rewrite the way on how to map the YAML to a plain String. As you know, a ConfigSource and a Converter only deal with plain Strings, so they are not prepared to handle a complex format like YAML. The old parser was just adding commas to represent lists and while it worked for simple cases, it was unable to represent nested container types. How could we represent and know how to parse at the Converter level a List of Lists? Please, check #216.

Unfortunately, this does break your Converter. On the other hand, I believe it will allow you to write a better code. I've specifically made sure to include a property to represent the complete YAML section. This will allow you to use a YAML parser directly in you converter. Check these examples: https://github.com/smallrye/smallrye-config/blob/4bbcf89ff170f20987d279f40f089a9b70ec903a/sources/yaml/src/test/java/io/smallrye/config/source/yaml/YamlConfigSourceTest.java

I hope it helps!

@martinvisser
Copy link
Author

Not entirely sure if this was how I should inject my config now, but with the example I got it working indeed.
Especially this: return new Yaml().loadAs(value, Users.class);
I inject the config now very easily with this, so no more ConfigProperties:

@Inject
org.eclipse.microprofile.config.Config config;

And read it as follows:

config.getValue("app.comics", Comic.class); // very similar POJO as Users.class from the example

Thanks!

@radcortez
Copy link
Member

radcortez commented Sep 14, 2020

Yes, that is one way. If you prefer your can inject the Config instance directly. It should work as well:

@Inject
@ConfigProperty(name="config.comic")
Comic comic;

Cheers!

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

No branches or pull requests

2 participants