Skip to content

[Bug] JavaBeanSerializeUtil.serialize() throws IllegalArgumentException on a Map containing a null key #16432

Description

@Amocy-Wang

Pre-check

  • I am sure that all the content I provide is in English.

Search before asking

  • I had searched in the issues and found no similar issues.

Apache Dubbo Component

Java SDK (apache/dubbo)

Dubbo Version

dubbo 3.2.20 (tag dubbo-3.2.20)

Steps to reproduce this issue

JavaBeanSerializeUtil.serialize() fails on a java.util.HashMap that contains a null key. HashMap permits exactly one null key, so this is a legal argument, and the serializer's own code has an explicit branch for it — but the value that branch produces is then rejected by a null check further down the same call chain.

import org.apache.dubbo.common.beanutil.JavaBeanSerializeUtil;
import java.util.HashMap;
import java.util.Map;

public class NullKeyRepro {
public static void main(String[] args) {
Map<Object, Object> ok = new HashMap<>();
ok.put("k", "v");
System.out.println("without null key: " + (JavaBeanSerializeUtil.serialize(ok) != null));

    Map<Object, Object> withNullKey = new HashMap<>();
    withNullKey.put(null, "value1");          // HashMap permits one null key
    JavaBeanSerializeUtil.serialize(withNullKey);   // throws
}

}

What you expected to happen

The map is serialised, with the null key represented as a null descriptor — which is what the code appears to intend.

Anything else

Actual

without null key: true
Exception in thread "main" java.lang.IllegalArgumentException: Property name is null

Root cause

dubbo-common/src/main/java/org/apache/dubbo/common/beanutil/JavaBeanSerializeUtil.java, lines 162–166:

map.forEach((key, value) -> {
    Object keyDescriptor   = key   == null ? null : createDescriptorIfAbsent(key,   accessor, cache);
    Object valueDescriptor = value == null ? null : createDescriptorIfAbsent(value, accessor, cache);
    descriptor.setProperty(keyDescriptor, valueDescriptor);
});

Line 163 deliberately produces null for a null key. Line 165 passes it to
JavaBeanDescriptor.setProperty, whose first statement (JavaBeanDescriptor.java,
lines 118–120) is:

public Object setProperty(Object propertyName, Object propertyValue) {
    notNull(propertyName, "Property name is null");
    return properties.put(propertyName, propertyValue);
}

So the null-producing branch and the null-rejecting guard sit in the same call
chain. Either the ternary on line 163 is dead for keys, or the guard is too strict
for this call site.

Notes

Not introduced by 3.2.20 — the code path is long-standing. The nearest existing
issue I found, #12248, is a different trigger (a null parameter causing an NPE
in generic-call bean mode).

Do you have a (mini) reproduction demo?

  • Yes, I have a minimal reproduction demo to help resolve this issue more effectively!

Are you willing to submit a pull request to fix on your own?

  • Yes I am willing to submit a pull request on my own!

Code of Conduct

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions