You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 23:14:52