Hi all I have the following yaml file example 'test.yml'
server:
port: 1000
someDate: /abcd
anotherConfig:
host: http://localhost:1000
fileList:
files:
- name: filea
filePath: \filea
- name: fileb
filePath: \fileb
---
anotherdoc:
data: 300
nestedData:
animal:
- name: dog
I read this file like so:
Yaml yaml = new Yaml();
InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("test.yml");
Iterable<Object> rules = yaml.loadAll(inputStream);
for (Object rule : rules) {
Map<String, Object> map = (Map<String, Object>) rule;
System.out.println("blah");
}
I am wondering how it is possible to get data from the rules object, e.g. if I wanted directly to search fileList/files or nestedData/animal so that I can use the path to get a List directly.
(Note yaml structure varies so cannot seem to use the entities method)
fileList/files is not a path in YAML terms because the YAML spec does not define any kind of path. You can:
use composeAll to get an iterator on Nodes. Nodes represent the YAML's structure and you can navigate through them similar to an XML DOM. You can then write a function that can walk a path.
plug in a modified parser that throws away everything not on your desired path, so that only your path is constructed. This example shows the basics for a slightly different use-case.
Write a custom constructor that throws away everything around the desired path. I am not entirely sure whether this is possible because the constructor API is a bit strange.
I suggest using method 1. It is flexible enough to deal with any kind of YAML structure, while being far less complex than the other methods. When you arrive at your desired Node, you can even use the Constructor class to construct a List or whatever you need from it.
Related
After a quick research, I found the following three libraries for parsing Toml files in java.
toml4j
tomlj
jackson-dataformats-text
What I am looking for is a library that can parse toml files without a corresponding POJO class. While both toml4j, and tomlj can achieve that, they do not seem to be maintained.
jackson-dataformats-text on the other is actively maintained but I can not parse a toml file without the corresponding POJO class.
Is there a way to create a dynamic class in java that I can use to parse any toml file?
If you just need to read a TOML file without a POJO, FasterXML Jackson libraries are a great choice. The simplest method is to just read the content as a java.util.Map:
final var tomlMapper = new TomlMapper();
final var data = tomlMapper.readValue(new File("config.toml"), Map.class);
After that, the content of the file will be available in data.
If you need even lower level parsing, all formats supported by FasterXML Jackson can be read using the stream API. In case you need that, read about the stream API on the core module: FasterXML/jackson-core, just make sure you use the right factory class (TomlFactory instead of JsonFactory).
I have a YAML string where one of the attributes looks like this:
description: |
this is my description //imagine there's a space after description
this is my description in the second line
In my Java code I read it into a JsonNode like this:
JsonNode node = new YamlMapper().readTree(yamlString);
I then do some changes to it and write it back to a string like this:
new YamlMapper().writeValueAsString(node))
The new string now looks like this:
"this is my description \nthis is my description in the second line\n"
So now in the YAML file you can see the added quotes + the new line character (\n) and everything is in one line. I expect it to return the original YAML like the one above.
This is how my YAML object mapper is configured:
new ObjectMapper(
new YAMLFactory()
.disable(YAMLGenerator.Feature.MINIMIZE_QUOTES))
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
If I remove the space after description in the original YAML, it works just fine
To serialize multiline text using jackson. Jackson introduced a new flag YAMLGenerator.Feature.LITERAL_BLOCK_STYLE since version 2.9, which can be turned on as:
new ObjectMapper(
new YAMLFactory().enable(YAMLGenerator.Feature.LITERAL_BLOCK_STYLE)
).writeValueAsString(new HashMap<String, String>(){{
put("key", "test1\ntest2\ntest3");
}});
The output won't be wrapped with quotes:
---
key: |-
test1
test2
test3
Note there is a few differences between "block scalars": |, |-, >..., you can check out at https://yaml-multiline.info/
Jackson's API is too high level to control the output in detail. You can use SnakeYAML directly (which is used by Jackson under the hood), but you need to go down to the node or event level of the API to control node style in the output.
See also: I want to load a YAML file, possibly edit the data, and then dump it again. How can I preserve formatting?
This answer shows general usage of SnakeYAML's event API to keep formatting; of course it's harder to do changes on a stream of events. You might instead want to work on the node graph, this answer has some example code showing how to load YAML to a node graph, process it, and write it back again.
We receive .csv files (both via ftp and email) each of which can be one of a few different formats (that can be determined by looking at the top line of the file). I am fairly new to Apache Camel but want to implement a content based router and unmarshal each to the relevant class.
My current solution is to break down the files to a lists of strings, manually use the first line to determine the type of file, and then use the rest of the strings to create relevant entity instances.
Are there a cleaner and better way?
You could use a POJO to implement the type check in whatever way works best for your files.
public String checkFileType(#Body File file) {
return determineFileType(file);
}
private String determineFileType(File file) {...}
Like this you can keep your route clean by separating the filetype check and any other part of processing. Because the filetype check is just metadata enrichment.
For example you could just set the return value as a message header by calling the bean)
.setHeader("fileType", method(fileTypeChecker))
Then you can route the files according to type easily by using the message header.
.choice()
.when(header("fileType").isEqualTo("foo"))
...
Is there a way to validate values in a YAML file while loading it in the code. The requirement is I have some elements in the YAML file which must have values. If the validation fails, then YAML should not be loaded.
I'm using snakeyaml library and heard there is a way to do this via Representer.
Code I'm currently using to load the YAML,
Reader in = new InputStreamReader(Files.newInputStream(file), StandardCharsets.UTF_8);
Yaml yaml = new Yaml();
yaml.setBeanAccess(BeanAccess.FIELD);
return yaml.loadAs(in, School.class);
Since you can have any value in a YAML file, you should load the file in a function, test the values and raise an error if the values are not what you want. Return the loaded data if they are.
This may have side-effects if your YAML has tags that create arbitrary objects, but checking during loading will not prevent that, as such object might have been created before you come to the value you want to check.
If you do have tags in your YAML and that is a real problem, then you would have to make a safe_load-er for the YAML file that can handle the tags (by creating normal mapping objects), then check the values and reload with full tag support.
Is it possible to read different property groups from a Java file, without manual processing?
By "manual" I mean reading the file line by line, detecting where the start of a property group is and then extracting the corresponding key-value pairs. In practice, this means reinventing (most of) the wheel that the Properties.load() method constitutes.
Essentially, what I am looking for is an easy way of reading, from a single file, multiple groups of properties, with each group being identifiable, so that it can be loaded in its own Java Properties object.
I you want to use java.util.Properties you can use prefixes. In .properties file:
group1.key1=valgroup1key1
group2.key1=valgroup2key1
group2.key2=valgroup2key2
and read them like this:
class PrefixedProperty extends Properties {
public String getProperty(String group, String key) {
return getProperty(group + '.' + key);
}
}
and using:
/* loading, initialization like for java.util.Properties */
String val = prefixedProperty.getProperty("group1", "key1");
You can also use ini4j with windows ini files.
Another, better way is using own, custom structured file (for example XML).