This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 29ccde6ef4574deec1ab3bb543bf3cd81108145c
Author: Paul King <[email protected]>
AuthorDate: Sun Apr 12 15:55:14 2026 +1000

    GROOVY-11926: Improve consistency of YAML functionality
---
 .../src/main/java/groovy/yaml/YamlBuilder.java     | 21 ++++++
 .../src/main/java/groovy/yaml/YamlSlurper.java     | 75 ++++++++++++++++++++++
 .../groovy-yaml/src/spec/doc/yaml-userguide.adoc   | 25 ++++++++
 .../spec/test/groovy/yaml/YamlBuilderTest.groovy   | 24 +++++++
 .../spec/test/groovy/yaml/YamlParserTest.groovy    | 48 ++++++++++++++
 5 files changed, 193 insertions(+)

diff --git a/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlBuilder.java 
b/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlBuilder.java
index a246c0b3cd..b5fb333b1a 100644
--- a/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlBuilder.java
+++ b/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlBuilder.java
@@ -18,6 +18,9 @@
  */
 package groovy.yaml;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
+import com.fasterxml.jackson.dataformat.yaml.YAMLGenerator;
 import groovy.json.JsonBuilder;
 import groovy.lang.Closure;
 import groovy.lang.GroovyObjectSupport;
@@ -43,6 +46,24 @@ public class YamlBuilder extends GroovyObjectSupport 
implements Writable {
         this.jsonBuilder = new JsonBuilder();
     }
 
+    /**
+     * Serializes a typed object to a YAML string using Jackson databinding.
+     * Supports {@code @JsonProperty} and {@code @JsonFormat} annotations.
+     *
+     * @param object the object to serialize
+     * @return the YAML string
+     * @since 6.0.0
+     */
+    public static String toYaml(Object object) {
+        try {
+            return new ObjectMapper(new YAMLFactory()
+                    .disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER))
+                    .writeValueAsString(object);
+        } catch (IOException e) {
+            throw new YamlRuntimeException(e);
+        }
+    }
+
     public Object getContent() {
         return jsonBuilder.getContent();
     }
diff --git a/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlSlurper.java 
b/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlSlurper.java
index 39a5165e34..6beeb4d3d0 100644
--- a/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlSlurper.java
+++ b/subprojects/groovy-yaml/src/main/java/groovy/yaml/YamlSlurper.java
@@ -18,9 +18,13 @@
  */
 package groovy.yaml;
 
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
 import groovy.json.JsonSlurper;
+import groovy.yaml.YamlRuntimeException;
 import org.apache.groovy.yaml.util.YamlConverter;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.InputStreamReader;
@@ -91,4 +95,75 @@ public class YamlSlurper {
         // note: convert to an input stream to allow the support of foreign 
file objects
         return parse(Files.newInputStream(path));
     }
+
+    /**
+     * Parse the content of the specified YAML text into a typed object using 
Jackson databinding.
+     * Supports {@code @JsonProperty} and {@code @JsonFormat} annotations for
+     * property mapping and type conversion.
+     *
+     * @param type the target type
+     * @param yaml the content of YAML
+     * @param <T> the target type
+     * @return a typed object
+     * @since 6.0.0
+     */
+    public <T> T parseTextAs(Class<T> type, String yaml) {
+        return parseAs(type, new StringReader(yaml));
+    }
+
+    /**
+     * Parse YAML from a reader into a typed object.
+     *
+     * @param type the target type
+     * @param reader the reader of YAML
+     * @param <T> the target type
+     * @return a typed object
+     * @since 6.0.0
+     */
+    public <T> T parseAs(Class<T> type, Reader reader) {
+        try (Reader r = reader) {
+            return new ObjectMapper(new YAMLFactory()).readValue(r, type);
+        } catch (IOException e) {
+            throw new YamlRuntimeException(e);
+        }
+    }
+
+    /**
+     * Parse YAML from an input stream into a typed object.
+     *
+     * @param type the target type
+     * @param stream the input stream of YAML
+     * @param <T> the target type
+     * @return a typed object
+     * @since 6.0.0
+     */
+    public <T> T parseAs(Class<T> type, InputStream stream) {
+        return parseAs(type, new InputStreamReader(stream));
+    }
+
+    /**
+     * Parse YAML from a file into a typed object.
+     *
+     * @param type the target type
+     * @param file the YAML file
+     * @param <T> the target type
+     * @return a typed object
+     * @since 6.0.0
+     */
+    public <T> T parseAs(Class<T> type, File file) throws IOException {
+        return parseAs(type, file.toPath());
+    }
+
+    /**
+     * Parse YAML from a path into a typed object.
+     *
+     * @param type the target type
+     * @param path the path to the YAML file
+     * @param <T> the target type
+     * @return a typed object
+     * @since 6.0.0
+     */
+    public <T> T parseAs(Class<T> type, Path path) throws IOException {
+        return parseAs(type, Files.newInputStream(path));
+    }
 }
diff --git a/subprojects/groovy-yaml/src/spec/doc/yaml-userguide.adoc 
b/subprojects/groovy-yaml/src/spec/doc/yaml-userguide.adoc
index 1d40327d2e..1334546019 100644
--- a/subprojects/groovy-yaml/src/spec/doc/yaml-userguide.adoc
+++ b/subprojects/groovy-yaml/src/spec/doc/yaml-userguide.adoc
@@ -90,6 +90,22 @@ The following table gives an overview of the YAML types and 
the corresponding Gr
 Whenever a value in YAML is `null`, `YamlSlurper` supplements it with the 
Groovy `null` value. This is in contrast to other
 YAML parsers that represent a `null` value with a library-provided singleton 
object.
 
+=== Typed parsing
+
+`YamlSlurper` can parse YAML directly into typed objects using Jackson 
databinding.
+Standard Jackson annotations such as `@JsonProperty` and `@JsonFormat` are 
supported
+for property mapping and type conversion:
+
+[source,groovy]
+----
+include::../test/groovy/yaml/YamlParserTest.groovy[tags=typed_class,indent=0]
+----
+
+[source,groovy]
+----
+include::../test/groovy/yaml/YamlParserTest.groovy[tags=typed_parsing,indent=0]
+----
+
 === Builders
 
 Another way to create YAML from Groovy is to use `YamlBuilder`. The builder 
provide a
@@ -99,3 +115,12 @@ DSL which allows to formulate an object graph which is then 
converted to YAML.
 ----
 include::../test/groovy/yaml/YamlBuilderTest.groovy[tags=build_text,indent=0]
 ----
+
+=== Typed writing
+
+`YamlBuilder.toYaml(object)` serializes a typed object directly to YAML using 
Jackson databinding:
+
+[source,groovy]
+----
+include::../test/groovy/yaml/YamlBuilderTest.groovy[tags=typed_writing,indent=0]
+----
diff --git 
a/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlBuilderTest.groovy 
b/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlBuilderTest.groovy
index 4d0602d7a4..8dbb5efe6c 100644
--- a/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlBuilderTest.groovy
+++ b/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlBuilderTest.groovy
@@ -53,4 +53,28 @@ records:
 '''
         // end::build_text[]
     }
+
+    // tag::typed_writing[]
+    static class ServerConfig {
+        String host
+        int port
+    }
+
+    void testToYaml() {
+        def config = new ServerConfig(host: 'localhost', port: 8080)
+        def yaml = YamlBuilder.toYaml(config)
+        assert yaml.contains('host:')
+        assert yaml.contains('localhost')
+        assert yaml.contains('port:')
+        assert yaml.contains('8080')
+    }
+    // end::typed_writing[]
+
+    void testTypedRoundTrip() {
+        def original = new ServerConfig(host: 'example.com', port: 443)
+        def yaml = YamlBuilder.toYaml(original)
+        def parsed = new YamlSlurper().parseTextAs(ServerConfig, yaml)
+        assert parsed.host == 'example.com'
+        assert parsed.port == 443
+    }
 }
\ No newline at end of file
diff --git 
a/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlParserTest.groovy 
b/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlParserTest.groovy
index b410bf10bc..6f289d7fca 100644
--- a/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlParserTest.groovy
+++ b/subprojects/groovy-yaml/src/spec/test/groovy/yaml/YamlParserTest.groovy
@@ -106,6 +106,54 @@ matrix:
 
     }
 
+    // tag::typed_class[]
+    static class ServerConfig {
+        String host
+        int port
+    }
+    // end::typed_class[]
+
+    void testParseTextAs() {
+        // tag::typed_parsing[]
+        def config = new YamlSlurper().parseTextAs(ServerConfig, '''\
+host: localhost
+port: 8080
+''')
+        assert config.host == 'localhost'
+        assert config.port == 8080
+        // end::typed_parsing[]
+    }
+
+    void testParseAsFromReader() {
+        def reader = new StringReader('host: localhost\nport: 8080')
+        def config = new YamlSlurper().parseAs(ServerConfig, reader)
+        assert config instanceof ServerConfig
+        assert config.host == 'localhost'
+        assert config.port == 8080
+    }
+
+    void testParseAsFromFile() {
+        def file = File.createTempFile('test', '.yml')
+        file.deleteOnExit()
+        file.text = 'host: localhost\nport: 9090'
+        def config = new YamlSlurper().parseAs(ServerConfig, file)
+        assert config.port == 9090
+    }
+
+    void testParseAsFromPath() {
+        def file = File.createTempFile('test', '.yml')
+        file.deleteOnExit()
+        file.text = 'host: example.com\nport: 443'
+        def config = new YamlSlurper().parseAs(ServerConfig, file.toPath())
+        assert config.host == 'example.com'
+    }
+
+    void testParseAsFromInputStream() {
+        def stream = new ByteArrayInputStream('host: localhost\nport: 
3000'.bytes)
+        def config = new YamlSlurper().parseAs(ServerConfig, stream)
+        assert config.port == 3000
+    }
+
     void testParseMultiDocs() {
         def ys = new YamlSlurper()
         def yaml = ys.parseText '''\

Reply via email to