从代表不同 类 的多个 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 字符串配置的方法预处理器 - 下一个处理器会正确解析它。
我想使用包含多个不同对象的单个 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 字符串配置的方法预处理器 - 下一个处理器会正确解析它。