SnakeYAML从1.3.2升级到2.0后Java代码编译报错求助
升级SnakeYAML至2.0后的编译问题修复
问题背景
将项目中SnakeYAML版本从1.3.2升级到2.0后,原有处理YAML序列化/反序列化的Java代码出现编译错误,原代码如下:
package uscrn.archive.environment; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.time.Instant; import java.time.YearMonth; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.AbstractConstruct; import org.yaml.snakeyaml.constructor.Constructor; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.ScalarNode; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Represent; import org.yaml.snakeyaml.representer.Representer; import uscrn.archive.io.FileReadFailed; import uscrn.archive.io.FileWriteFailed; /** * An implementation of WorkLog that persists information to the local file * system in the Yaml text format. This allows the application to record * the work it does, and later query the record for decision making. */ public class YamlFileWorkLog implements WorkLog { private final File file; private final TreeMap<YearMonth, Instant> entries; public YamlFileWorkLog(File file) { this.file = file; this.entries = load(file); } public YamlFileWorkLog(Path path){ this(path.toFile()); } @SuppressWarnings("unchecked") private TreeMap<YearMonth, Instant> load(File file) { TreeMap<YearMonth, Instant> result = new TreeMap<>(); try(InputStream input = new FileInputStream(file)){ Map<YearMonth, Instant> loaded = (Map<YearMonth, Instant>) yaml().load(input); if(loaded != null){ result.putAll(loaded); } return result; } catch (IOException e) { throw new FileReadFailed(file.toPath(), e); } } @Override public Instant lookup(YearMonth month) { return entries.get(month); } @Override public void record(YearMonth month, Instant timestamp) { entries.put(month, timestamp); } @Override public void clear() { entries.clear(); } @Override public boolean hasRecorded(YearMonth month) { return entries.containsKey(month); } @Override public void save() { try(FileWriter output = new FileWriter(file)){ yaml().dump(entries, output); } catch (IOException e) { throw new FileWriteFailed(file.toPath(), e); } } @Override public int size() { return entries.size(); } @Override public Instant latestTimestamp(){ Instant timestamp = null; for(Entry<YearMonth, Instant> entry : entries.entrySet()){ if(timestamp == null || timestamp.isBefore(entry.getValue())){ timestamp = entry.getValue(); } } return timestamp; } @Override public String toString(){ return yaml().dump(entries); } /** * Returns an Yaml object that is configured to read and write WorkLog * information. * @return A configured Yaml object. */ private Yaml yaml(){ DumperOptions options = new DumperOptions(); options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); return new Yaml(new LogConstructor(), new LogRepresenter(), options); } /** * A class that is used by the Yaml library to convert WorkLog entries * into Yaml constructs, which can then be written out as text. */ private class LogRepresenter extends Representer { public LogRepresenter() { this.representers.put(YearMonth.class, new Represent(){ @Override public Node representData(Object data) { YearMonth month = (YearMonth) data; return representScalar(new Tag("!month"), month.toString()); } } ); this.representers.put(Instant.class, new Represent(){ @Override public Node representData(Object data) { Instant timestamp = (Instant) data; return representScalar(Tag.TIMESTAMP, timestamp.toString()); } } ); } } /** * A class that is used by the Yaml library to convert text into WorkLog * entries. */ private class LogConstructor extends SafeConstructor { public LogConstructor() { this.yamlConstructors.put(new Tag("!month"), new AbstractConstruct(){ @Override public Object construct(Node node) { String val = (String) constructScalar((ScalarNode) node); Integer year = Integer.parseInt(val.substring(0, 4)); Integer month = Integer.parseInt(val.substring(5, 7)); return YearMonth.of(year, month); } } ); this.yamlConstructors.put(Tag.TIMESTAMP, new AbstractConstruct(){ @Override public Object construct(Node node) { String val = (String) constructScalar((ScalarNode) node); return Instant.parse(val); } } ); } } }
编译错误信息
- 错误1:
Representer类的构造方法无法应用于给定类型;需要:DumperOptions;找到:无参数;原因:实参与形参列表长度不符。 - 错误2:
SafeConstructor类的构造方法无法应用于给定类型;需要:LoaderOptions;找到:无参数;原因:实参与形参列表长度不符。
修复方案
SnakeYAML 2.0做了不兼容API变更:Representer和SafeConstructor的无参构造函数被移除,必须传入对应的配置类实例。具体修改如下:
1. 添加LoaderOptions导入
在import区域新增:
import org.yaml.snakeyaml.LoaderOptions;
2. 修改LogRepresenter类
更新构造方法,接收DumperOptions并传递给父类:
private class LogRepresenter extends Representer { public LogRepresenter(DumperOptions options) { super(options); this.representers.put(YearMonth.class, new Represent(){ @Override public Node representData(Object data) { YearMonth month = (YearMonth) data; return representScalar(new Tag("!month"), month.toString()); } } ); this.representers.put(Instant.class, new Represent(){ @Override public Node representData(Object data) { Instant timestamp = (Instant) data; return representScalar(Tag.TIMESTAMP, timestamp.toString()); } } ); } }
3. 修改LogConstructor类
更新构造方法,接收LoaderOptions并传递给父类:
private class LogConstructor extends SafeConstructor { public LogConstructor(LoaderOptions options) { super(options); this.yamlConstructors.put(new Tag("!month"), new AbstractConstruct(){ @Override public Object construct(Node node) { String val = (String) constructScalar((ScalarNode) node); Integer year = Integer.parseInt(val.substring(0, 4)); Integer month = Integer.parseInt(val.substring(5, 7)); return YearMonth.of(year, month); } } ); this.yamlConstructors.put(Tag.TIMESTAMP, new AbstractConstruct(){ @Override public Object construct(Node node) { String val = (String) constructScalar((ScalarNode) node); return Instant.parse(val); } } ); } }
4. 修改yaml()方法
创建LoaderOptions实例,并传递给LogConstructor,同时将DumperOptions传递给LogRepresenter:
private Yaml yaml(){ DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); LoaderOptions loaderOptions = new LoaderOptions(); return new Yaml(new LogConstructor(loaderOptions), new LogRepresenter(dumperOptions), dumperOptions); }
完整修复后代码
package uscrn.archive.environment; import java.io.File; import java.io.FileInputStream; import java.io.FileWriter; import java.io.IOException; import java.io.InputStream; import java.nio.file.Path; import java.time.Instant; import java.time.YearMonth; import java.util.Map; import java.util.Map.Entry; import java.util.TreeMap; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.LoaderOptions; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.AbstractConstruct; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.nodes.Node; import org.yaml.snakeyaml.nodes.ScalarNode; import org.yaml.snakeyaml.nodes.Tag; import org.yaml.snakeyaml.representer.Represent; import org.yaml.snakeyaml.representer.Representer; import uscrn.archive.io.FileReadFailed; import uscrn.archive.io.FileWriteFailed; /** * An implementation of WorkLog that persists information to the local file * system in the Yaml text format. This allows the application to record * the work it does, and later query the record for decision making. */ public class YamlFileWorkLog implements WorkLog { private final File file; private final TreeMap<YearMonth, Instant> entries; public YamlFileWorkLog(File file) { this.file = file; this.entries = load(file); } public YamlFileWorkLog(Path path){ this(path.toFile()); } @SuppressWarnings("unchecked") private TreeMap<YearMonth, Instant> load(File file) { TreeMap<YearMonth, Instant> result = new TreeMap<>(); try(InputStream input = new FileInputStream(file)){ Map<YearMonth, Instant> loaded = (Map<YearMonth, Instant>) yaml().load(input); if(loaded != null){ result.putAll(loaded); } return result; } catch (IOException e) { throw new FileReadFailed(file.toPath(), e); } } @Override public Instant lookup(YearMonth month) { return entries.get(month); } @Override public void record(YearMonth month, Instant timestamp) { entries.put(month, timestamp); } @Override public void clear() { entries.clear(); } @Override public boolean hasRecorded(YearMonth month) { return entries.containsKey(month); } @Override public void save() { try(FileWriter output = new FileWriter(file)){ yaml().dump(entries, output); } catch (IOException e) { throw new FileWriteFailed(file.toPath(), e); } } @Override public int size() { return entries.size(); } @Override public Instant latestTimestamp(){ Instant timestamp = null; for(Entry<YearMonth, Instant> entry : entries.entrySet()){ if(timestamp == null || timestamp.isBefore(entry.getValue())){ timestamp = entry.getValue(); } } return timestamp; } @Override public String toString(){ return yaml().dump(entries); } /** * Returns an Yaml object that is configured to read and write WorkLog * information. * @return A configured Yaml object. */ private Yaml yaml(){ DumperOptions dumperOptions = new DumperOptions(); dumperOptions.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); LoaderOptions loaderOptions = new LoaderOptions(); return new Yaml(new LogConstructor(loaderOptions), new LogRepresenter(dumperOptions), dumperOptions); } /** * A class that is used by the Yaml library to convert WorkLog entries * into Yaml constructs, which can then be written out as text. */ private class LogRepresenter extends Representer { public LogRepresenter(DumperOptions options) { super(options); this.representers.put(YearMonth.class, new Represent(){ @Override public Node representData(Object data) { YearMonth month = (YearMonth) data; return representScalar(new Tag("!month"), month.toString()); } } ); this.representers.put(Instant.class, new Represent(){ @Override public Node representData(Object data) { Instant timestamp = (Instant) data; return representScalar(Tag.TIMESTAMP, timestamp.toString()); } } ); } } /** * A class that is used by the Yaml library to convert text into WorkLog * entries. */ private class LogConstructor extends SafeConstructor { public LogConstructor(LoaderOptions options) { super(options); this.yamlConstructors.put(new Tag("!month"), new AbstractConstruct(){ @Override public Object construct(Node node) { String val = (String) constructScalar((ScalarNode) node); Integer year = Integer.parseInt(val.substring(0, 4)); Integer month = Integer.parseInt(val.substring(5, 7)); return YearMonth.of(year, month); } } ); this.yamlConstructors.put(Tag.TIMESTAMP, new AbstractConstruct(){ @Override public Object construct(Node node) { String val = (String) constructScalar((ScalarNode) node); return Instant.parse(val); } } ); } } }
内容的提问来源于stack exchange,提问作者user1634050
相关产品推荐
相关产品推荐

