Skip to content

Commit 3c4a84b

Browse files
authored
Add YAML utils (#709)
Signed-off-by: Valentin Delaye <jonesbusy@users.noreply.github.com>
1 parent 03049a8 commit 3c4a84b

3 files changed

Lines changed: 210 additions & 0 deletions

File tree

pom.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@
250250
<groupId>tools.jackson.dataformat</groupId>
251251
<artifactId>jackson-dataformat-toml</artifactId>
252252
</dependency>
253+
<dependency>
254+
<groupId>tools.jackson.dataformat</groupId>
255+
<artifactId>jackson-dataformat-yaml</artifactId>
256+
</dependency>
253257

254258
<!-- Test dependencies -->
255259
<dependency>
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/*-
2+
* =LICENSE=
3+
* ORAS Java SDK
4+
* ===
5+
* Copyright (C) 2024 - 2025 ORAS
6+
* ===
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* =LICENSEEND=
19+
*/
20+
21+
package land.oras.utils;
22+
23+
import java.io.IOException;
24+
import java.nio.charset.StandardCharsets;
25+
import java.nio.file.Files;
26+
import java.nio.file.Path;
27+
import land.oras.exception.OrasException;
28+
import org.jspecify.annotations.NullMarked;
29+
import tools.jackson.core.JacksonException;
30+
import tools.jackson.databind.ObjectMapper;
31+
import tools.jackson.dataformat.yaml.YAMLMapper;
32+
33+
/**
34+
* Utility class for YAML operations.
35+
* Use Jackson 3 internally for YAML operations
36+
*/
37+
@NullMarked
38+
public final class YamlUtils {
39+
40+
/**
41+
* TOML mapper instance
42+
*/
43+
private static final ObjectMapper yamlMapper;
44+
45+
/**
46+
* Utils class
47+
*/
48+
private YamlUtils() {
49+
// Hide constructor
50+
}
51+
52+
static {
53+
yamlMapper = YAMLMapper.builder().build();
54+
}
55+
56+
/**
57+
* Convert an object to a YAML string
58+
* @param object The object to convert
59+
* @return The YAML string
60+
*/
61+
public static String toYaml(Object object) {
62+
try {
63+
return yamlMapper.writeValueAsString(object);
64+
} catch (JacksonException e) {
65+
throw new OrasException("Unable to convert object to YAML string", e);
66+
}
67+
}
68+
69+
/**
70+
* Convert a YAML string to an object
71+
* @param yaml The YAML string
72+
* @param clazz The class of the object
73+
* @param <T> The type of the object
74+
* @return The object
75+
*/
76+
public static <T> T fromYaml(String yaml, Class<T> clazz) {
77+
try {
78+
return yamlMapper.readValue(yaml, clazz);
79+
} catch (JacksonException e) {
80+
throw new OrasException("Unable to parse YAML string", e);
81+
}
82+
}
83+
84+
/**
85+
* Read a YAML file and convert its contents to an object.
86+
* The file at the given {@code path} is read as UTF-8 YAML and deserialized into the specified type.
87+
* @param path The path to the YAML file
88+
* @param clazz The class of the object
89+
* @param <T> The type of the object
90+
* @return The object deserialized from the YAML file
91+
*/
92+
public static <T> T fromYaml(Path path, Class<T> clazz) {
93+
try {
94+
return yamlMapper.readValue(Files.readString(path, StandardCharsets.UTF_8), clazz);
95+
} catch (IOException | JacksonException e) {
96+
throw new OrasException("Unable to read YAML from file", e);
97+
}
98+
}
99+
}
Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
/*-
2+
* =LICENSE=
3+
* ORAS Java SDK
4+
* ===
5+
* Copyright (C) 2024 - 2025 ORAS
6+
* ===
7+
* Licensed under the Apache License, Version 2.0 (the "License");
8+
* you may not use this file except in compliance with the License.
9+
* You may obtain a copy of the License at
10+
*
11+
* http://www.apache.org/licenses/LICENSE-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS,
15+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16+
* See the License for the specific language governing permissions and
17+
* limitations under the License.
18+
* =LICENSEEND=
19+
*/
20+
21+
package land.oras.utils;
22+
23+
import static org.junit.jupiter.api.Assertions.assertEquals;
24+
import static org.junit.jupiter.api.Assertions.assertNotNull;
25+
import static org.junit.jupiter.api.Assertions.assertThrows;
26+
27+
import java.io.IOException;
28+
import java.nio.file.Files;
29+
import java.nio.file.Path;
30+
import java.util.LinkedHashMap;
31+
import java.util.Map;
32+
import land.oras.exception.OrasException;
33+
import org.junit.jupiter.api.Test;
34+
import org.junit.jupiter.api.io.CleanupMode;
35+
import org.junit.jupiter.api.io.TempDir;
36+
import org.junit.jupiter.api.parallel.Execution;
37+
import org.junit.jupiter.api.parallel.ExecutionMode;
38+
39+
@Execution(ExecutionMode.CONCURRENT)
40+
class YamlUtilsTest {
41+
42+
/**
43+
* Temporary dir
44+
*/
45+
@TempDir(cleanup = CleanupMode.ON_SUCCESS)
46+
private static Path dir;
47+
48+
@Test
49+
void failToParseYamlString() {
50+
assertThrows(OrasException.class, () -> YamlUtils.fromYaml("not a yaml: too, bar: test", Object.class));
51+
}
52+
53+
@Test
54+
@SuppressWarnings("unchecked")
55+
void shouldParseYaml() {
56+
String yamlMap =
57+
"""
58+
---
59+
key1: "value1"
60+
key2: "value2"
61+
""";
62+
Map<String, String> map = YamlUtils.fromYaml(yamlMap, Map.class);
63+
assertNotNull(map);
64+
assertEquals(2, map.size());
65+
assertEquals("value1", map.get("key1"));
66+
assertEquals("value2", map.get("key2"));
67+
}
68+
69+
@Test
70+
@SuppressWarnings("unchecked")
71+
void shouldParseYamlFile() throws IOException {
72+
String yamlMap =
73+
"""
74+
---
75+
key1: "value1"
76+
key2: "value2"
77+
""";
78+
Path yamlFile = dir.resolve("test.yaml");
79+
Files.writeString(yamlFile, yamlMap);
80+
Map<String, String> map = YamlUtils.fromYaml(yamlFile, Map.class);
81+
assertNotNull(map);
82+
assertEquals(2, map.size());
83+
assertEquals("value1", map.get("key1"));
84+
assertEquals("value2", map.get("key2"));
85+
}
86+
87+
@Test
88+
@SuppressWarnings("unchecked")
89+
void shouldFailToParseYamlFile() {
90+
assertThrows(OrasException.class, () -> YamlUtils.fromYaml(Path.of("foo.yaml"), Map.class));
91+
}
92+
93+
@Test
94+
void shouldConvertToYaml() {
95+
Map<String, String> map = new LinkedHashMap<>();
96+
map.put("key1", "value1");
97+
map.put("key2", "value2");
98+
String yaml = YamlUtils.toYaml(map);
99+
assertNotNull(yaml);
100+
String expected = """
101+
---
102+
key1: "value1"
103+
key2: "value2"
104+
""";
105+
assertEquals(expected, yaml);
106+
}
107+
}

0 commit comments

Comments
 (0)