mirai icon indicating copy to clipboard operation
mirai copied to clipboard

在 PluginData 支持可空类型

Open Moyuyanli opened this issue 2 years ago • 6 comments

问题描述

问题:JavaAutoSavePluginData自动保存是没有按照我定义的类型存入变量

public class SessionData extends JavaAutoSavePluginData{

   /**
  * 唯一构造
     */
    public static final SessionData INSTANCE = new SessionData();

    /**
     * 文件名
     */
    public SessionData() {
        super("SessionData");
    }


    public final Value<Map<String,Map<String, String>>> sessionlist = typedValue("sessionlist",
            createKType(Map.class,
                    createKType(String.class),
                    createKType(Map.class,
                            createKType(String.class), createKType(String.class)
                    )
            ));
}

图片

结果:在不存在数据的时候,可以运行一次,当有数据存入的时候,结果就是报错,提示无法识别,然后插件启动失败

图片

请问一下这样的情况怎么解决,或者能不再指明一条在mirai-console的插件其他数据持久化的方法!

复现

当时发生过一次,现在再照个以上代码测试不确定能再次触发

mirai-core 版本

2.1.0-71ec418

bot-protocol

ANDROID_PHONE

其他组件版本

"net.mamoe:mirai-console" v2.11.0 "net.mamoe:mirai-console-terminal" v2.11.0 "net.mamoe:mirai-core-all" v2.11.0 "org.itxtech:mcl-addon" v2.0.2 "org.bouncycastle:bcprov-jdk15on" v1.64 "net.mamoe:mirai-api-http" v2.5.2 "net.mamoe:chat-command" v0.5.1 "xyz.cssxsh.mirai:mirai-administrator" v1.1.0 "xyz.cssxsh.mirai:mirai-administrator" has newer version "1.2.0"

系统日志

No response

网络日志

No response

补充信息

log-74.log log-75.log log-76.log log-72.log log-73.log

Moyuyanli avatar Jun 17 '22 14:06 Moyuyanli

Caused by: net.mamoe.yamlkt.YamlDecodingException: Top-level decoder: Yaml Null: Unexpected null
    messageChain: null
                     ^ at line 5, column 23

Karlatemp avatar Jun 18 '22 03:06 Karlatemp

你需要声明类型为可空

    public final Value<Map<String , Map<String, String>>> sessionlist = typedValue("sessionlist",
            createKType(Map.class,
                    createKType(String.class),
                    createKType(Map.class,
                            createKType(String.class), createKType(String.class,  /* nullable */ true)
                    )
            ));

cssxsh avatar Jun 18 '22 04:06 cssxsh

试了下,用了nullable: true,该Test非首次运行依然无法通过

public class MockAutoSaveDataTest {

    public static class MockPlugin extends JavaPlugin {
        public static final MockPlugin INSTANCE = new MockPlugin();
        public MockPlugin() {
            super(new JvmPluginDescriptionBuilder("org.test.test", "1.0.0").build());
        }
    }

    public static class MockAutoSaveData extends JavaAutoSavePluginConfig {
        public static final MockAutoSaveData INSTANCE = new MockAutoSaveData();
        public MockAutoSaveData() {
            super("MockAutoSaveData");
        }
        public final Value<Map<String, String>> content = typedValue("content",
                createKType(Map.class,
                        createKType(String.class),
                        createKType(String.class, true)
                )
        );
    }

    @Before
    public void beforeTest() {
        MiraiConsoleTerminalLoader.INSTANCE.startAsDaemon(new MiraiConsoleImplementationTerminal());
        PluginManager.INSTANCE.loadPlugin(MockPlugin.INSTANCE);
        PluginManager.INSTANCE.enablePlugin(MockPlugin.INSTANCE);
    }

    @Test
    public void testData() {
        MockPlugin.INSTANCE.reloadPluginData(MockAutoSaveData.INSTANCE);
        /*
        1. 首次运行,yml文件生成内容为“content: {}”,通过测试
        2. 人工编辑yml文件内容为“
        content:
          foo: null
        ”时,也应能通过测试
         */
        assertEquals(null, MockAutoSaveData.INSTANCE.content.get().get("foo"));
    }
}
> Task :test

Top-level decoder: deserializing nested class for 'content' in 'MockAutoSaveData'
  foo: null
          ^ at line 2, column 12

net.mamoe.yamlkt.YamlDecodingException: Top-level decoder: deserializing nested class for 'content' in 'MockAutoSaveData'
  foo: null
          ^ at line 2, column 12

	at net.mamoe.yamlkt.internal.YamlUtils__ContextualExceptionKt.contextualDecodingException(ContextualException.kt:154)
	at net.mamoe.yamlkt.internal.YamlUtils.contextualDecodingException(Unknown Source)
	at net.mamoe.yamlkt.internal.YamlDecoder$AbstractDecoder.decodeSerializableElement(YamlDecoder.kt:181)
	at kotlinx.serialization.encoding.CompositeDecoder$DefaultImpls.decodeSerializableElement$default(Decoding.kt:535)
	at net.mamoe.mirai.console.internal.data.PluginDataImpl$updaterSerializer$1.deserialize(PluginDataImpl.kt:85)
	at net.mamoe.mirai.console.internal.data.PluginDataImpl$updaterSerializer$1.deserialize(PluginDataImpl.kt:47)
	at net.mamoe.yamlkt.Yaml.decodeFromString(Yaml.kt:162)
	at net.mamoe.mirai.console.internal.data.MultiFilePluginDataStorageImpl.load(MultiFilePluginDataStorageImpl.kt:40)
	at net.mamoe.mirai.console.plugin.jvm.AbstractJvmPlugin.reloadPluginData(AbstractJvmPlugin.kt:50)
	at org.example.mirai.plugin.MockAutoSaveDataTest.testData(MockAutoSaveDataTest.java:50)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:47)
	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
	at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:44)
	at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
	at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
	at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:271)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:70)
	at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:50)
	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.runTestClass(JUnitTestClassExecutor.java:110)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:58)
	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecutor.execute(JUnitTestClassExecutor.java:38)
	at org.gradle.api.internal.tasks.testing.junit.AbstractJUnitTestClassProcessor.processTestClass(AbstractJUnitTestClassProcessor.java:62)
	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
	at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
	at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:119)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.base/java.lang.reflect.Method.invoke(Method.java:566)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
	at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:182)
	at org.gradle.internal.remote.internal.hub.MessageHubBackedObjectConnection$DispatchWrapper.dispatch(MessageHubBackedObjectConnection.java:164)
	at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:414)
	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
	at org.gradle.internal.concurrent.ManagedExecutorImpl$1.run(ManagedExecutorImpl.java:48)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at org.gradle.internal.concurrent.ThreadFactoryImpl$ManagedThreadRunnable.run(ThreadFactoryImpl.java:56)
	at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.NullPointerException: Parameter specified as non-null is null: method net.mamoe.mirai.console.internal.data.StringValueImpl.setValue, parameter v
	at net.mamoe.mirai.console.internal.data.StringValueImpl.setValue(_PrimitiveValueDeclarations.kt)
	at net.mamoe.mirai.console.internal.data.StringValueImpl.set(_PrimitiveValueDeclarations.kt:348)
	at net.mamoe.mirai.console.data.PluginDataKt.valueFromKType(PluginData.kt:332)
	at net.mamoe.mirai.console.internal.data.ValueFromKTypeImplKt$valueFromKTypeImpl$3.invoke(valueFromKTypeImpl.kt:70)
	at net.mamoe.mirai.console.internal.data.ValueFromKTypeImplKt$valueFromKTypeImpl$3.invoke(valueFromKTypeImpl.kt:61)
	at net.mamoe.mirai.console.internal.data.ShadowMap.putAll(collectionUtil.kt:86)
	at net.mamoe.mirai.console.internal.data.CollectionUtilKt$observable$ObservableMap.putAll(collectionUtil.kt:400)
	at net.mamoe.mirai.console.internal.data.CompositeValueImplKt.patchImpl(CompositeValueImpl.kt:190)
	at net.mamoe.mirai.console.internal.data.CompositeValueImplKt.tryPatch(CompositeValueImpl.kt:208)
	at net.mamoe.mirai.console.internal.data.CompositeMapValueImpl.setValueBySerializer(CompositeValueImpl.kt:157)
	at net.mamoe.mirai.console.internal.data.CompositeMapValueImpl.setValueBySerializer(CompositeValueImpl.kt:127)
	at net.mamoe.mirai.console.internal.data._PrimitiveValueDeclarationsKt.setValueBySerializer(_PrimitiveValueDeclarations.kt:32)
	at net.mamoe.mirai.console.data.SerializableValue$Companion$serializableValueWith$$inlined$map$1.deserialize(serializerHelper.kt:175)
	at net.mamoe.yamlkt.internal.YamlDecoder$AbstractDecoder.decodeSerializableElement(YamlDecoder.kt:179)
	... 55 more

hundun000 avatar Jun 18 '22 04:06 hundun000

或者test2()

    @Test
    public void testData2() {
        MockPlugin.INSTANCE.reloadPluginData(MockAutoSaveData.INSTANCE);

        /*
        不论是否首次运行,尝试写入null map-value
         */
        Map<String, String> nullValueMap = new HashMap<>();
        nullValueMap.put("foo", null);
        MockAutoSaveData.INSTANCE.content.set(nullValueMap);

        // assert 方法正常结束
    }

也是出现

Caused by: java.lang.NullPointerException: Parameter specified as non-null is null: method net.mamoe.mirai.console.internal.data.StringValueImpl.setValue, parameter v
	at net.mamoe.mirai.console.internal.data.StringValueImpl.setValue(_PrimitiveValueDeclarations.kt)
	at net.mamoe.mirai.console.internal.data.StringValueImpl.set(_PrimitiveValueDeclarations.kt:348)

hundun000 avatar Jun 18 '22 05:06 hundun000

我们还没支持在map/set等里面使用可空类型

Karlatemp avatar Jun 18 '22 06:06 Karlatemp

那确实是得支持的

Him188 avatar Jun 18 '22 07:06 Him188