我有以下方法用于将对象转换为yaml
表示(我可以例如打印到控制台)
@Nonnull
private String outputObject(@Nonnull final ObjectToPrint packageSchedule) {
DumperOptions options = new DumperOptions();
options.setAllowReadOnlyProperties(true);
options.setPrettyFlow(true);
return new Yaml(new Constructor(), new JodaTimeRepresenter(), options).dump(ObjectToPrint);
}
一切都很好,但对于ObjectToPrint
结构中包含的某些对象,我得到的内容类似于引用名称,而不是真实的对象内容,例如。
!!com.blah.blah.ObjectToPrint
businessYears:
- businessYearMonths: 12
ppiYear: &id001 {
endDate: 30-06-2013,
endYear: 2013,
startDate: 01-07-2012,
startYear: 2012
}
ppiPeriod:
ppiYear: *id001
endDate: 27-03-2014
startDate: 21-06-2013
units: 24.000
number: 1
从上面的示例中可以看出,我打印了ppiYear
个对象(标记为$id001
),ppiPeriod
中使用了相同的对象,但只打印了参考名称,不是对象内容。
每次我在我的结构中使用该对象时,如何打印对象内容,我希望将其转换为yaml(ObjectToPrint
)。
PS。最好不要打印参考名称(&id001
),但这不是至关重要的
答案 0 :(得分:5)
这是因为您在不同的地方引用了同一个对象。为避免这种情况,您需要创建这些对象的副本。 Yaml没有一个标志来关闭它,因为在循环引用的情况下你可能会进入无限循环。 但是,您可能会调整Yaml源代码以忽略双引用:
看看Serializer line~170方法serializeNode:
...
if ( this.serializedNodes.contains(node) ) {
this.emmitter.emit( new AliasEvent( ... ) );
} else {
serializedNodes.add(node); // <== Replace with myHook(serializedNodes,node);
...
void myHook(serializedNodes,node) {
if ( node's class != myClass(es) to avoid ) {
serializedNodes.add(node);
}
如果你找到一种方法来避免Yaml将节点放入serializedNodes集合中,你的问题就会得到解决,但是如果循环引用,你的程序将循环无穷。
最佳解决方案是添加一个钩子,避免只注册你想要写成的类。
答案 1 :(得分:1)
作为一种不改变SnakeYAML源代码的方法,您可以定义:
public class NonAnchorRepresenter extends Representer {
public NonAnchorRepresenter() {
this.multiRepresenters.put(Map.class, new RepresentMap() {
public Node representData(Object data) {
return representWithoutRecordingDescendents(data, super::representData);
}
});
}
protected Node representWithoutRecordingDescendents(Object data, Function<Object,Node> worker) {
Map<Object,Node> representedObjectsOnEntry = new LinkedHashMap<Object,Node>(representedObjects);
try {
return worker.apply(data);
} finally {
representedObjects.clear();
representedObjects.putAll(representedObjectsOnEntry);
}
}
}
并将其用作例如new Yaml(new SafeConstructor(), new NonAnchorRepresenter());
这只做地图, 在必要时使用锚点,即地图指的是祖先。如果需要,它将需要类似于集合和列表的扩展。 (就我而言,这是空地图,是最大的罪犯。)
(在Representer.representData
上的SnakeYAML代码库中更容易处理,查看选项,例如setAllowAnchors
默认为true,如果不允许,则在递归后重置representedObjects
。我在https://stackoverflow.com/a/18419489/109079处讨论了无限循环的可能性,但使用此策略检测对父映射的任何引用并快速失败将是直截了当的。)