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

Gson映射自定义枚举遇非法值触发空指针异常问题咨询

Fixing Gson Enum Parsing NPE for Unknown Values

I've run into this exact issue before—Gson's default behavior for unrecognized enum values is to set the field to null, which then causes NPEs when you try to use that field later. Let's break down why this happens and how to fix it.

Why the Exception Happens

When Gson encounters a string like "unknown" that doesn't match any of your Status enum's @SerializedName values, it sets the status field in StatusData to null. If your code tries to call methods on that null reference (like status.getClass()), you get the null pointer exception you're seeing.

Solution 1: Custom TypeAdapter for Your Status Enum

The cleanest fix is to create a custom TypeAdapter that handles unknown values by mapping them to a dedicated UNKNOWN enum value instead of null.

First, update your enum to include the UNKNOWN value:

public enum Status {
    @SerializedName("on")
    ON,
    @SerializedName("off")
    OFF,
    UNKNOWN; // No @SerializedName needed unless you want to map a specific string to it
}

Next, write the TypeAdapter:

public class StatusTypeAdapter extends TypeAdapter<Status> {
    @Override
    public void write(JsonWriter out, Status value) throws IOException {
        if (value == null) {
            out.nullValue();
            return;
        }
        // Get the serialized name from the enum's annotation
        try {
            Field field = value.getClass().getField(value.name());
            SerializedName annotation = field.getAnnotation(SerializedName.class);
            out.value(annotation != null ? annotation.value() : value.name().toLowerCase());
        } catch (NoSuchFieldException | IllegalAccessException e) {
            out.value(value.name().toLowerCase());
        }
    }

    @Override
    public Status read(JsonReader in) throws IOException {
        String inputValue = in.nextString();
        // Check each enum value's SerializedName for a match
        for (Status status : Status.values()) {
            try {
                Field field = status.getClass().getField(status.name());
                SerializedName annotation = field.getAnnotation(SerializedName.class);
                if (annotation != null && annotation.value().equals(inputValue)) {
                    return status;
                }
            } catch (NoSuchFieldException e) {
                // Ignore, shouldn't happen for valid enums
            }
        }
        // Return UNKNOWN for unrecognized values
        return Status.UNKNOWN;
    }
}

Finally, register this adapter when creating your Gson instance:

Gson gson = new GsonBuilder()
        .registerTypeAdapter(Status.class, new StatusTypeAdapter())
        .create();

Now, when parsing {"status":"unknown"}, the status field will be set to Status.UNKNOWN instead of null, eliminating the NPE.

Solution 2: Generic Enum TypeAdapterFactory (For Multiple Enums)

If you have multiple enums in your project that need this handling, create a reusable factory instead of writing individual adapters. This assumes all your enums have an UNKNOWN value:

public class EnumTypeAdapterFactory implements TypeAdapterFactory {
    @Override
    public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> type) {
        Class<T> rawType = (Class<T>) type.getRawType();
        if (!rawType.isEnum()) {
            return null; // Skip non-enum types
        }

        Map<String, T> serializedValueToEnum = new HashMap<>();
        Map<T, String> enumToSerializedValue = new HashMap<>();
        T defaultUnknownValue = null;

        // Populate mappings and find the UNKNOWN value
        for (T enumConstant : rawType.getEnumConstants()) {
            try {
                Field field = rawType.getField(enumConstant.name());
                SerializedName annotation = field.getAnnotation(SerializedName.class);
                String serializedValue = annotation != null ? annotation.value() : enumConstant.name();
                
                serializedValueToEnum.put(serializedValue, enumConstant);
                enumToSerializedValue.put(enumConstant, serializedValue);
                
                if ("UNKNOWN".equals(enumConstant.name())) {
                    defaultUnknownValue = enumConstant;
                }
            } catch (NoSuchFieldException e) {
                // Log or handle if needed, but enums should always have their fields
            }
        }

        final T finalDefault = defaultUnknownValue;

        return new TypeAdapter<T>() {
            @Override
            public void write(JsonWriter out, T value) throws IOException {
                if (value == null) {
                    out.nullValue();
                } else {
                    out.value(enumToSerializedValue.get(value));
                }
            }

            @Override
            public T read(JsonReader in) throws IOException {
                String input = in.nextString();
                T match = serializedValueToEnum.get(input);
                return match != null ? match : finalDefault;
            }
        };
    }
}

Register the factory like this:

Gson gson = new GsonBuilder()
        .registerTypeAdapterFactory(new EnumTypeAdapterFactory())
        .create();

This will handle all your enums consistently, mapping unrecognized values to their UNKNOWN constant.

Key Takeaway

Avoid relying on Gson's default enum parsing if your JSON might contain unexpected values. By using a custom adapter or factory, you can control how unknown values are handled and prevent those pesky NPEs.

内容的提问来源于stack exchange,提问作者TheSecond

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.22 08:04:42