未发布版本 v6.3.0-SNAPSHOT。 本页内容来自 alpha 分支,随时可能变更,不属于任何已发布版本。

commit 0ed0d39 · 注入于 2026-10-06 00:56 UTC

Skip to content

配置转换器 ​

可用版本

本页描述 alpha 分支尚未发布的配置转换行为,自 v6.3.0 起可用。

ConfigConverter<T> 在声明 Java 值与普通配置数据之间双向转换。通过模块扫描包内的 @ConfigConverterFor 在配置构造前登记。文档存储和注册表协调桥接是内部实现,不是模块事务 API。

转换器 API ​

公开类型位于 com.ultikits.ultitools.config.convert。

java
public interface ConfigConverter<T> {
    Object toPlain(T value, ConversionContext ctx) throws ConversionException;
    T fromPlain(Object plain, ConversionContext ctx) throws ConversionException;
}

ConversionContext 提供 file()、完整键不可变列表 path() 和包含泛型的 declaredType()。用 ctx.toPlain(nested) 转换嵌套运行值,ctx.fromPlain(nested, type) 转换嵌套声明类型。ConversionException 构造参数是原因、文件、完整路径、声明类型,可选底层原因;它提供定位转换失败信息。自 v6.3.0 起,传入的原因会以 (reason: ...) 显示在给服主的跳过警告里,键名像密钥时与值一起脱敏,所以原因要写给服主看:说明该设置期望什么。

普通数据与往返 ​

自 v6.3.0 起,普通数据是 null、String、Boolean、Integer、Long、BigInteger、Double、这些数据组成的列表和字符串键映射。不能返回 Bukkit MemorySection 或任意 Java bean。UUID、枚举、BigDecimal 由内置转换器用文字表示。注册的 Bukkit ConfigurationSerializable 使用序列化别名和普通映射;读入 Object 字段的值保持普通数据,不自动构造 Bukkit 对象。

转换器对声明类型中集合和数组均无 null 元素的每个值 x 必须满足正向相等。类型化集合(包括 List<Object>)和引用数组(包括 Object[])写入时省略 null 元素,每个字段记录一条定位警告;从文件读取时,类型化集合里的 null 元素被跳过并给出定位警告,与 6.2 一致。映射的 null 值与整个 null 字段仍可往返。声明为 Object 的普通数据槽保持原样。反向相等适用于转换器输出的规范普通值 p(p = toPlain(x)):

text
fromPlain(toPlain(x)) equals x
toPlain(fromPlain(p)) equals p

转换器可以接受非规范输入 q。其规范形式为 toPlain(fromPlain(q)),规范化必须稳定:

text
fromPlain(toPlain(fromPlain(q))) equals fromPlain(q)

已批准的转换行为不变:数值转 String、数字文字转 int/float、"false" 转 boolean、重复元素转 Set。无需保留原始非规范表示。比较的是语义值,不是对象身份;普通数值按值比较。读时加十、写时不减十违反正向相等。面板映射叶编辑和三方重载依靠正向相等保持未改兄弟项。测试 null、嵌套值、转换器输出的普通值及接受的非规范输入。

注册与查找 ​

自 v6.3.0 起,发现的转换器需要是 public 顶层类,带 public 无参构造器;扫描器不发现嵌套转换器。转换器在配置实体前实例化,不是 IoC bean;不能依赖注入或模块初始化副作用。放进 @UltiToolsModule(scanBasePackages = {...}) 的包。重复目标类型拒绝加载并列两个类。@ConfigConverterFor(value = T.class, exact = true) 只服务精确类型,默认 exact = false 也服务子类型。

查找顺序为:

  1. 显式非默认 @ConfigEntry(parser = X.class) 使用旧适配器。
  2. 模块注册表:精确类、非 exact 父类链、非 exact 接口。
  3. 框架注册表,同样顺序。
  4. 泛型集合、映射、数组、枚举、Object 转换。
  5. 有登记别名的 Bukkit ConfigurationSerializable 后备转换。
  6. 无转换器:选中配置类型检查拒绝模块加载。

完整继承泛型在读取或创建任何选中配置文件前检查。配方值不支持时诊断结构如下(名称来自相关模块):

text
Module UltiRecipe, file config/recipes.yml, key "recipes": no config converter for ...RecipeDefinition (declared as java.util.Map<java.lang.String, ...RecipeDefinition>). Register one with @ConfigConverterFor in the module scan packages, or declare a supported plain-data type.

实际消息列完整类型。声明类型可转换但整个值无效时使用最初声明默认值,无效列表/映射元素跳过并定位警告。未知运行对象在保存时拒绝且不改目标。绑定警告隐藏密钥形状值。

转换器示例 ​

把以下值/字段成员放进 example.config 包的 MigrationExample 类(imports 放在外层类前)。转换器单独放进同一扫描包的 public 顶层文件 TokenConverter.java,见第二段;不能嵌套在外层类中。这是未发布分支内联片段,不是对已发布构件编译的引用。严格的单文字键映射使两条等式成立,包括 null 文字。

java
import java.util.Objects;
import com.ultikits.ultitools.annotations.ConfigEntry;

public static class Token {
    public String text;
    public Token(String text) { this.text = text; }
    @Override public boolean equals(Object other) {
        return other instanceof Token && Objects.equals(text, ((Token) other).text);
    }
    @Override public int hashCode() { return Objects.hashCode(text); }
}
@ConfigEntry(path = "token")
private Token token = new Token("hello");
java
// TokenConverter.java: a separate top-level source file.
package example.config;

import java.util.LinkedHashMap;
import java.util.Map;
import example.config.MigrationExample.Token;
import com.ultikits.ultitools.config.convert.ConfigConverter;
import com.ultikits.ultitools.config.convert.ConfigConverterFor;
import com.ultikits.ultitools.config.convert.ConversionContext;
import com.ultikits.ultitools.config.convert.ConversionException;

@ConfigConverterFor(Token.class)
public class TokenConverter implements ConfigConverter<Token> {
    public TokenConverter() { }
    @Override public Object toPlain(Token value, ConversionContext ctx) {
        if (value == null) { return null; }
        Map<String, Object> plain = new LinkedHashMap<>();
        plain.put("text", value.text);
        return plain;
    }
    @Override public Token fromPlain(Object plain, ConversionContext ctx)
            throws ConversionException {
        if (plain == null) { return null; }
        if (!(plain instanceof Map)) { throw failure(ctx); }
        Map<?, ?> map = (Map<?, ?>) plain;
        if (map.size() != 1 || !map.containsKey("text")
                || !(map.get("text") == null || map.get("text") instanceof String)) {
            throw failure(ctx);
        }
        return new Token((String) map.get("text"));
    }
    private ConversionException failure(ConversionContext ctx) {
        return new ConversionException("Expected only a text key containing text or null",
                ctx.file(), ctx.path(), ctx.declaredType());
    }
}

旧解析器迁移 ​

自 v6.3.0 起,六项公告首次带 @Deprecated(since = "6.3.0", forRemoval = true):ConfigEntry#parser()、interfaces.Parser、interfaces.ObjectConfigSerializer、interfaces.impl.pasers.ConfigParser、DefaultConfigParser、StringHashMapParser。公告下个 MINOR 6.4.0 删除,已发布的包名拼写 pasers 不变。

DefaultConfigParser 子类曾使用继承的配置节读取器和反射写入器。上述 Token 的旧声明可以是:

java
import java.util.Map;

public static class TokenParser extends
        com.ultikits.ultitools.interfaces.impl.pasers.DefaultConfigParser {
    @Override public Object parse(Object raw) {
        Map<?, ?> values = (Map<?, ?>) super.parse(raw);
        return new Token((String) values.get("text"));
    }
}
@ConfigEntry(path = "token", parser = TokenParser.class)
private Token oldToken = new Token("hello");

用转换器示例的字段和类替换旧字段/解析器。显式旧覆盖仍收到隔离 Bukkit 配置节(保留点号拆分),序列化输出仍须通过普通数据边界。默认 DefaultConfigParser.class 注解值改表示注册表转换,不增加任意自定义类原始透传例外。

完整映射键、文件保护、整份输出、原子/备份保存、重载和实体 accessor 删除见配置文件。存储已知限制仍记录在 #578 和 #580。

贡献者

暂无相关贡献者

基于 MIT 许可发布