繁体   English   中英

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

[英]How to parse part of .yaml file to object in 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

我还有一个适用于interesting-setup部分的 POJO class

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

我只想加载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

关键部分是当我有一个 Object (Map<String, Object>) 并想将其转换为我的最终 class 时。为此 - 我需要将其序列化为 String,因此过程如下所示:

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

那么我能以某种方式使用Yaml到 map Map<String, Object>到另一个 class 吗? 我在 API 中看不到这个?

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

您可以尝试调整它并定义一个容器基础 class,其中仅包含您需要支持的字段。 例如考虑以下 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 +
        '}';
  }
}

其中, InterestingSetup是你自己的class:

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 +
        '}';
  }
}

有了这些 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());
  }
}

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

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);

作为替代方法,您可以使用 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());

Container class 与上面所示相同,只是添加了@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 +
        '}';
  }
}

所需的工件可以从Maven下载为以下依赖项:

<dependency>
    <groupId>com.fasterxml.jackson.dataformat</groupId>
    <artifactId>jackson-dataformat-yaml</artifactId>
    <version>2.13.1</version>
</dependency>

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM