This is an automated email from the ASF dual-hosted git repository.

zhangliang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new e11281fcfc0 Normalize null collections to empty in YAML constructor to 
avoid NPE (#37450)
e11281fcfc0 is described below

commit e11281fcfc076aaea950edc212ab5de528f33b49
Author: Liang Zhang <[email protected]>
AuthorDate: Sat Dec 20 23:30:52 2025 +0800

    Normalize null collections to empty in YAML constructor to avoid NPE 
(#37450)
    
    * Normalize null collections to empty in YAML constructor to avoid NPE
    
    * Normalize null collections to empty in YAML constructor to avoid NPE
    
    * Normalize null collections to empty in YAML constructor to avoid NPE
    
    * Normalize null collections to empty in YAML constructor to avoid NPE
    
    * Normalize null collections to empty in YAML constructor to avoid NPE
---
 .../infra/yaml/data/pojo/YamlRowStatistics.java    |  3 +-
 .../constructor/ShardingSphereYamlConstructor.java | 47 ++++++++++++++++++++++
 .../ClasspathResourceDirectoryReaderTest.java      |  4 +-
 .../infra/util/yaml/YamlEngineTest.java            | 15 +++++++
 .../YamlNullCollectionConfigurationFixture.java}   | 18 +++++----
 .../src/test/resources/yaml/null-collections.yaml  | 21 ++++++++++
 6 files changed, 98 insertions(+), 10 deletions(-)

diff --git 
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
 
b/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
index fdde3e390dd..27c16efa378 100644
--- 
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
+++ 
b/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
@@ -21,6 +21,7 @@ import lombok.Getter;
 import lombok.Setter;
 import org.apache.shardingsphere.infra.util.yaml.YamlConfiguration;
 
+import java.util.ArrayList;
 import java.util.List;
 
 /**
@@ -32,5 +33,5 @@ public final class YamlRowStatistics implements 
YamlConfiguration {
     
     private String uniqueKey;
     
-    private List<Object> rows;
+    private List<Object> rows = new ArrayList<>();
 }
diff --git 
a/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
 
b/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
index 29c18fd9c67..54dbe85e60b 100644
--- 
a/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
+++ 
b/infra/util/src/main/java/org/apache/shardingsphere/infra/util/yaml/constructor/ShardingSphereYamlConstructor.java
@@ -18,6 +18,7 @@
 package org.apache.shardingsphere.infra.util.yaml.constructor;
 
 import com.google.common.base.Preconditions;
+import lombok.SneakyThrows;
 import org.apache.shardingsphere.infra.spi.ShardingSphereServiceLoader;
 import org.apache.shardingsphere.infra.spi.type.typed.TypedSPILoader;
 import 
org.apache.shardingsphere.infra.util.yaml.shortcuts.ShardingSphereYamlShortcuts;
@@ -25,11 +26,18 @@ import org.yaml.snakeyaml.LoaderOptions;
 import org.yaml.snakeyaml.TypeDescription;
 import org.yaml.snakeyaml.constructor.Construct;
 import org.yaml.snakeyaml.constructor.Constructor;
+import org.yaml.snakeyaml.introspector.Property;
 import org.yaml.snakeyaml.nodes.Node;
+import org.yaml.snakeyaml.nodes.MappingNode;
 
+import java.util.Collection;
+import java.util.Collections;
 import java.util.HashMap;
+import java.util.List;
 import java.util.Map;
 import java.util.Optional;
+import java.util.Properties;
+import java.util.Set;
 
 /**
  * ShardingSphere YAML constructor.
@@ -65,6 +73,45 @@ public final class ShardingSphereYamlConstructor extends 
Constructor {
         return construct.isPresent() ? construct.get() : 
super.getConstructor(node);
     }
     
+    @Override
+    public Object constructObject(final Node node) {
+        Object result = super.constructObject(node);
+        if (isMappingNode(node, result)) {
+            
getPropertyUtils().getProperties(result.getClass()).stream().filter(Property::isWritable).forEach(each
 -> setEmptyCollectionIfNull(result, each));
+        }
+        return result;
+    }
+    
+    private static boolean isMappingNode(final Node node, final Object target) 
{
+        return null != target && node instanceof MappingNode && !(target 
instanceof Map) && !(target instanceof Collection);
+    }
+    
+    @SneakyThrows(Exception.class)
+    private void setEmptyCollectionIfNull(final Object target, final Property 
property) {
+        if (null == property.get(target)) {
+            property.set(target, getEmptyCollection(property.getType()));
+        }
+    }
+    
+    private Object getEmptyCollection(final Class<?> propertyType) {
+        if (Properties.class.isAssignableFrom(propertyType)) {
+            return new Properties();
+        }
+        if (Map.class.isAssignableFrom(propertyType)) {
+            return Collections.emptyMap();
+        }
+        if (Set.class.isAssignableFrom(propertyType)) {
+            return Collections.emptySet();
+        }
+        if (List.class.isAssignableFrom(propertyType)) {
+            return Collections.emptyList();
+        }
+        if (Collection.class.isAssignableFrom(propertyType)) {
+            return Collections.emptyList();
+        }
+        return null;
+    }
+    
     @Override
     protected Class<?> getClassForName(final String className) throws 
ClassNotFoundException {
         Preconditions.checkArgument(className.equals(rootClass.getName()), 
"Class `%s` is not accepted", className);
diff --git 
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
 
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
index f8c58ff35e2..8ee6924309f 100644
--- 
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
+++ 
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/directory/ClasspathResourceDirectoryReaderTest.java
@@ -41,9 +41,9 @@ class ClasspathResourceDirectoryReaderTest {
     @Test
     void assertReadTest() {
         List<String> resourceNameList = 
ClasspathResourceDirectoryReader.read("yaml").collect(Collectors.toList());
-        assertThat(resourceNameList.size(), is(5));
+        assertThat(resourceNameList.size(), is(6));
         assertThat(resourceNameList, hasItems("yaml/accepted-class.yaml", 
"yaml/customized-obj.yaml", "yaml/empty-config.yaml",
-                "yaml/shortcuts-fixture.yaml", "yaml/fixture/fixture.yaml"));
+                "yaml/shortcuts-fixture.yaml", "yaml/null-collections.yaml", 
"yaml/fixture/fixture.yaml"));
     }
     
     @Test
diff --git 
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
 
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
index 962321f1fab..f93275f98be 100644
--- 
a/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
+++ 
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/YamlEngineTest.java
@@ -18,6 +18,7 @@
 package org.apache.shardingsphere.infra.util.yaml;
 
 import org.apache.shardingsphere.infra.util.file.SystemResourceFileUtils;
+import 
org.apache.shardingsphere.infra.util.yaml.fixture.YamlNullCollectionConfigurationFixture;
 import 
org.apache.shardingsphere.infra.util.yaml.fixture.shortcuts.YamlShortcutsConfigurationFixture;
 import org.junit.jupiter.api.Test;
 import org.yaml.snakeyaml.composer.ComposerException;
@@ -95,6 +96,20 @@ class YamlEngineTest {
         assertTrue(actual.isEmpty());
     }
     
+    @Test
+    void assertUnmarshalWithNullCollections() {
+        String yamlContent = 
SystemResourceFileUtils.readFile("yaml/null-collections.yaml");
+        YamlNullCollectionConfigurationFixture actual = 
YamlEngine.unmarshal(yamlContent, YamlNullCollectionConfigurationFixture.class);
+        assertNotNull(actual.getMap());
+        assertTrue(actual.getMap().isEmpty());
+        assertNotNull(actual.getSet());
+        assertTrue(actual.getSet().isEmpty());
+        assertNotNull(actual.getList());
+        assertTrue(actual.getList().isEmpty());
+        assertNotNull(actual.getCollection());
+        assertTrue(actual.getCollection().isEmpty());
+    }
+    
     @Test
     void assertMarshal() {
         YamlShortcutsConfigurationFixture actual = new 
YamlShortcutsConfigurationFixture();
diff --git 
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
 
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/fixture/YamlNullCollectionConfigurationFixture.java
similarity index 72%
copy from 
infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
copy to 
infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/fixture/YamlNullCollectionConfigurationFixture.java
index fdde3e390dd..1497fbfe20d 100644
--- 
a/infra/common/src/main/java/org/apache/shardingsphere/infra/yaml/data/pojo/YamlRowStatistics.java
+++ 
b/infra/util/src/test/java/org/apache/shardingsphere/infra/util/yaml/fixture/YamlNullCollectionConfigurationFixture.java
@@ -15,22 +15,26 @@
  * limitations under the License.
  */
 
-package org.apache.shardingsphere.infra.yaml.data.pojo;
+package org.apache.shardingsphere.infra.util.yaml.fixture;
 
 import lombok.Getter;
 import lombok.Setter;
 import org.apache.shardingsphere.infra.util.yaml.YamlConfiguration;
 
+import java.util.Collection;
 import java.util.List;
+import java.util.Map;
+import java.util.Set;
 
-/**
- * YAML row statistics.
- */
 @Getter
 @Setter
-public final class YamlRowStatistics implements YamlConfiguration {
+public final class YamlNullCollectionConfigurationFixture implements 
YamlConfiguration {
+    
+    private Map<String, String> map;
+    
+    private Set<String> set;
     
-    private String uniqueKey;
+    private List<String> list;
     
-    private List<Object> rows;
+    private Collection<String> collection;
 }
diff --git a/infra/util/src/test/resources/yaml/null-collections.yaml 
b/infra/util/src/test/resources/yaml/null-collections.yaml
new file mode 100644
index 00000000000..f17a40e0805
--- /dev/null
+++ b/infra/util/src/test/resources/yaml/null-collections.yaml
@@ -0,0 +1,21 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements.  See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License.  You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+map:
+set:
+list:
+collection:

Reply via email to