从代表不同 类 的多个 YAML 文档中解析单个 POJO

Parse a single POJO from multiple YAML documents representing different classes

我想使用包含多个不同对象的单个 YAML 文件 - 用于不同的应用程序。我需要获取一个对象以获得 MyClass1 的实例,忽略 MyClass2、MyClass3 等的其余文档。某种 selective 反序列化:现在这个 class,然后那个。 .. MyClass2、MyClass3 的结构对于使用 MyClass1 的应用程序来说是完全未知的。当然,该文件始终是有效的 YAML。

YAML 可以是我们实现这种多 class 容器所需的任何结构。首选解析工具是snakeyaml。

合理吗?我怎样才能忽略除一个对象之外的所有对象?

UPD:将所有 "document" 替换为 "object"。我认为我们必须谈论包含多个 不同 结构对象的单个 YAML 文档。更多的是,解析器只知道 1 个结构并想忽略其余的。

UDP2:我觉得用snakeyaml是不可能的。无论如何,我们必须读取所有对象 - select 稍后需要的对象。但也许我错了。

UPD2:示例配置文件

--- 
- 
  exportConfiguration781: 
    attachmentFieldName: "name"
    baseSftpInboxPath: /home/user/somedir/
    somebool: false
    days: 9999
    expected: 
      - ABC w/o quotes
      - "Cat ABC"
      - "Some string"
    dateFormat: yyyy-MMdd-HHmm
    user: someuser
- 
  anotherConfiguration: 
    k1: v1
    k2: 
      - v21
      - v22

这对于 SnakeYAML 来说绝对是可能的,尽管不是微不足道的。以下是您需要做的事情的概要:

首先,让我们看看 loading 用 SnakeYAML 做了什么。这是 YAML class 的重要部分:

private Object loadFromReader(StreamReader sreader, Class<?> type) {
    Composer composer = new Composer(new ParserImpl(sreader), resolver, loadingConfig);
    constructor.setComposer(composer);
    return constructor.getSingleData(type);
}

composer 将 YAML 输入解析为 Nodes。为此,它不需要了解您的 classes 的结构,因为每个节点都是 ScalarNode、SequenceNode 或 MappingNode,它们只代表 YAML 结构。

constructor 获取由 composer 生成的根节点并从中生成本机 POJO。所以你想要做的是在节点图到达构造函数之前丢弃它们。

最简单的方法可能是从 Composer 派生并覆盖两个方法,如下所示:

public class MyComposer extends Composer {
    private final int objIndex;

    public MyComposer(Parser parser, Resolver resolver, int objIndex) {
        super(parser, resolver);
        this.objIndex = objIndex;
    }

    public MyComposer(Parser parser, Resolver resolver, LoaderOptions loadingConfig, int objIndex) {
        super(parser, resolver, loadingConfig);
        this.objIndex = objIndex;
    }


    @Override
    public Node getNode() {
        return strip(super.getNode());
    }

    private Node strip(Node input) {
        return ((SequenceNode)input).getValue().get(objIndex);
    }
}

strip 实现只是一个例子。在这种情况下,我假设您的 YAML 看起来像这样(对象内容是任意的):

- {first: obj}
- {second: obj}
- {third: obj}

您只需 select 您实际想要通过其在序列中的索引反序列化的对象。但是你也可以有更复杂的东西,比如搜索算法。

现在你有了自己的作曲家,你可以做

Constructor constructor = new Constructor();
// assuming we want to get the object at index 1 (i.e. second object)
Composer composer = new MyComposer(new ParserImpl(sreader), new Resolver(), 1);
constructor.setComposer(composer);
MyObject result = (MyObject)constructor.getSingleData(MyObject.class);

@flyx 的回答对我很有帮助,通过覆盖一些方法打开了解决库(在我们的例子中是 snakeyaml)限制的方法。非常感谢!它很可能有一个最终的解决方案——但不是现在。此外,下面的简单解决方案是可靠的,即使我们找到了完整的库入侵解决方案也应该考虑。

我决定通过双提法来解决这个任务,抱歉,正在处理配置文件。想象后者由几个部分组成,每个部分都由唯一的标记分隔符标记。为了保持YAML-likenes,它可能是

---
#this is a unique key for the configuration A
<some YAML document>
---
#this is another key for the configuration B
<some YAML document

第一步是预处理。对于给定的字符串 fileString 和字符串键(和 DELIMITER = "\n---\n"。例如)我们 select 具有键定义配置的子字符串:

int begIndex;                                                                       
do {                                                                                
  begIndex= fileString.indexOf(DELIMITER);                                          
  if (begIndex == -1) {                                                             
    break;                                                                          
  }                                                                                 
  if (fileString.startsWith(DELIMITER + key, begIndex)) {                           
    fileString = fileString.substring(begIndex + DELIMITER.length() + key.length());
    break;                                                                          
  }                                                                                 
  // spoil alien delimiter and repeat search                                        
  fileString = fileString.replaceFirst(DELIMITER, " ");                             
} while (true);                                                                     
int endIndex = fileString.indexOf(DELIMITER);                                       
if (endIndex != -1) {                                                               
  fileString = fileString.substring(0, endIndex);                                   
} 

现在我们将 fileString 提供给简单的 YAML 解析

ExportConfiguration configuration = new Yaml(new Constructor(ExportConfiguration.class))
    .loadAs(fileString, ExportConfiguration.class);

这次我们有一个文档必须共同响应 ExportConfiguration class。

注1:配置文件其余部分的结构甚至内容都没有任何作用。这是主要思想,在单个文件中获得独立配置

注2:其余配置可能是JSON或XML或其他。我们有一个 returns 字符串配置的方法预处理器 - 下一个处理器会正确解析它。