如何将 .yaml 文件的一部分解析为 SnakeYAML 中的对象?

Ben*_*min 6 java spring snakeyaml

我有一个 yaml 文件,例如:

# this is the part I don't care about
config:
  key-1: val-1
other-config:
  lang: en
  year: 1906
# below is the only part I care about
interesting-setup:
  port: 1234
  validation: false
  parts:
    - on-start: backup
      on-stop: say-goodbye
Run Code Online (Sandbox Code Playgroud)

我还有一个适合该interesting-setup部分的 POJO 类

public class InterestingSetup {
    int port;
    boolean validation;
    List<Map<String, String>> parts;
}
Run Code Online (Sandbox Code Playgroud)

我想只加载该interesting-setup部分(与 Spring 中类似@ConfigurationProperties("interesting-setup")

目前我正在这样做:

Map<String, Object> yamlConfig = yaml.load(yamlFile);            # loading the whole file to Map with Object values
Object interestingObject = yamlConfig.get("interesting-setup");  # loading 'interesting-setup' part as an object
Map<String, Object> interestingMap = (Map<String, Object>);      # Casting object to Map<String, Object>
String yamlDumped = yaml.dump(interestingMap);                   # Serialization to String
InterestingSetup finalObject = yaml.load(yamlDumped);            # Getting final object from String
Run Code Online (Sandbox Code Playgroud)

关键部分是当我有一个对象 (Map<String, Object>) 并希望将其转换为我的最终类时。为此,我需要将其序列化为字符串,因此过程如下所示:

File -> Map<String, Object> -> Object -> Map<String, Object> -> String -> FinalClass 我想避免相同数据的反序列化和再次序列化。

那么我可以用某种方式Yaml将 映射Map<String, Object>到另一个类吗?我在 API 中看不到这个?

jcc*_*ero 2

AFAIK,SnakeYAML 库没有提供直接的方法来做到这一点。

您可以尝试调整它并仅使用您需要支持的字段定义容器基类。例如,考虑以下 POJO:

public class Container {
  private InterestingSetup interestingSetup;

  public InterestingSetup getInterestingSetup() {
    return interestingSetup;
  }

  public void setInterestingSetup(InterestingSetup interestingSetup) {
    this.interestingSetup = interestingSetup;
  }

  @Override
  public String toString() {
    return "Container{" +
        "interestingSetup=" + interestingSetup +
        '}';
  }
}
Run Code Online (Sandbox Code Playgroud)

哪里,InterestingSetup是你自己的班级:

import java.util.List;
import java.util.Map;

public class InterestingSetup {
  private int port;
  private boolean validation;
  private List<Map<String, String>> parts;

  public int getPort() {
    return port;
  }

  public void setPort(int port) {
    this.port = port;
  }

  public boolean isValidation() {
    return validation;
  }

  public void setValidation(boolean validation) {
    this.validation = validation;
  }

  public List<Map<String, String>> getParts() {
    return parts;
  }

  public void setParts(List<Map<String, String>> parts) {
    this.parts = parts;
  }

  @Override
  public String toString() {
    return "InterestingSetup{" +
        "port=" + port +
        ", validation=" + validation +
        ", parts=" + parts +
        '}';
  }
}
Run Code Online (Sandbox Code Playgroud)

有了这些 bean,以下代码就可以按照您的要求工作:

import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.nio.charset.StandardCharsets;

import org.yaml.snakeyaml.TypeDescription;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
import org.yaml.snakeyaml.representer.Representer;

public class Main {
  public static void main(String... args) throws UnsupportedEncodingException {
    String yamlString =
        "# this is the part I don't care about\n" +
        "config:\n" +
        "  key-1: val-1\n" +
        "other-config:\n" +
        "  lang: en\n" +
        "  year: 1906\n" +
        "# below is the only part I care about\n" +
        "interesting-setup:\n" +
        "  port: 1234\n" +
        "  validation: false\n" +
        "  parts:\n" +
        "    - on-start: backup\n" +
        "      on-stop: say-goodbye";

    // Skip unknown properties
    Representer representer = new Representer();
    representer.getPropertyUtils().setSkipMissingProperties(true);

    // Define the target object type
    Constructor constructor = new Constructor(Container.class);
    TypeDescription containerTypeDescription = new TypeDescription(Container.class);

    // Define how the interesting-setup property should be processed
    containerTypeDescription.substituteProperty("interesting-setup", InterestingSetup.class,
        "getInterestingSetup", "setInterestingSetup");
    constructor.addTypeDescription(containerTypeDescription);

    // Finally, parse the YAML
    Yaml yaml = new Yaml(constructor, representer);
    InputStream inputStream = new ByteArrayInputStream(yamlString.getBytes(StandardCharsets.UTF_8));;
    Container container = yaml.load(inputStream);
    System.out.println(container.getInterestingSetup());
  }
}
Run Code Online (Sandbox Code Playgroud)

也许,一个更简单的解决方案将包括使用某种方法,允许您在给定一堆字段及其相应值的情况下在 bean 中设置适当的信息InterestedSetup。您可以使用 Reflection API 来实现这一点。Apache Commons 类中的populate方法BeansUtils很方便:

Map<String, Object> yamlConfig = yaml.load(yamlFile); 
Object interestingObject = yamlConfig.get("interesting-setup");
Map<String, Object> interestingMap = (Map<String, Object>);
InterestingSetup finalObject = BeanUtils.populate(interestingMap);
Run Code Online (Sandbox Code Playgroud)

作为替代方法,您可以使用 Jackson 来处理 YAML 文件。代码将类似于:

ObjectMapper mapper = new ObjectMapper(new YAMLFactory());
// As the helper object Container doesn't contain all the properties
// it is necessary to indicate that fact to the library to avoid
// errors
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
Container container = mapper.readValue(yamlString, Container.class);
System.out.println(container.getInterestingSetup());
Run Code Online (Sandbox Code Playgroud)

该类Container与上面提供的类相同,但添加了注释@JsonProperty以便成功处理该interesting-setup字段:

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;

// Instead of mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
// you can annotate the class with @JsonIgnoreProperties(ignoreUnknown = true)
// to avoid errors related to unknown properties
public class Container {

  @JsonProperty("interesting-setup")
  private InterestingSetup interestingSetup;

  public InterestingSetup getInterestingSetup() {
    return interestingSetup;
  }

  public void setInterestingSetup(InterestingSetup interestingSetup) {
    this.interestingSetup = interestingSetup;
  }

  @Override
  public String toString() {
    return "Container{" +
        "interestingSetup=" + interestingSetup +
        '}';
  }
}
Run Code Online (Sandbox Code Playgroud)

所需的工件可以从Maven下载,如下所示:

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
    <version>2.13.1</version>
</dependency>
Run Code Online (Sandbox Code Playgroud)