Skip to content

[BUG][JAVA][okhttp-gson] add regression tests for null additionalProperties values - #25611

Open
Inf1mum wants to merge 5 commits into
OpenAPITools:masterfrom
Inf1mum:fix/24393-gson-null-additional-properties
Open

Inf1mum wants to merge 5 commits into
OpenAPITools:masterfrom
Inf1mum:fix/24393-gson-null-additional-properties

Conversation

@Inf1mum

@Inf1mum Inf1mum commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #24393

Context

#24393 reports that the okhttp-gson CustomTypeAdapterFactory in pojo.mustache throws
java.lang.IllegalStateException: Not a JSON Object: null when a model with additionalProperties: true
serializes an undeclared property whose value is null (e.g. an unknown field deserialized from a
server response that was JSON null): gson.toJsonTree(null) returns JsonNull.INSTANCE and the
else branch called getAsJsonObject() on it.

The template guard proposed in the issue is already on master: it was merged in #24573 (based on
#24395), which added the jsonElement.isJsonNull() check writing JsonNull.INSTANCE plus the
com.google.gson.JsonNull import, and regenerated the samples. That PR did not reference #24393
and did not add tests, which left the issue open without regression coverage.

This PR closes that gap with tests only, so the issue can be closed on merge. No template or
generated-sample changes are needed.

Changes (tests only)

  • modules/openapi-generator/src/test/resources/3_0/java/okhttp-gson-null-additional-properties.yaml:
    minimal spec with an additionalProperties: true model.
  • modules/openapi-generator/src/test/java/org/openapitools/codegen/java/JavaOkHttpGsonNullAdditionalPropertiesTest.java:
    generates that model with the java/okhttp-gson generator, compiles the generated
    CustomTypeAdapterFactory against the real Gson runtime (same fixture approach as
    JavaOkHttpGsonOneOfPrimitiveArrayTest), and asserts:
  • samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java:
    new testAdditionalPropertiesNullValue reproducing the issue's exact repro with the generated
    petstore AdditionalPropertiesClass (putAdditionalProperty("unknownField", null) + toJson()).

Regenerated samples

None required. This PR contains no template changes; the okhttp-gson samples already carry the
JsonNull guard since #24573, and re-running ./bin/generate-samples.sh bin/configs/java-okhttp-gson.yaml
on this branch produces no diff in samples/client/petstore/java/okhttp-gson (verified).

Verification

  • ./mvnw -pl modules/openapi-generator -am test -Dtest=JavaOkHttpGsonNullAdditionalPropertiesTest passes.
  • mvn test -Dtest=JSONTest in samples/client/petstore/java/okhttp-gson passes (34 tests, including the new one).

PR checklist

  • Read the contribution guidelines.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    Tests-only PR: the project modules and CLI jar were built with ./mvnw package -DskipTests,
    and the targeted regen ./bin/generate-samples.sh bin/configs/java-okhttp-gson.yaml produces
    no diff on this branch (see "Regenerated samples" above). All changed files are committed.
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request.
    (@mentions intentionally omitted)

Summary by cubic

Adds regression tests for the already-merged fix to #24393, where the okhttp-gson CustomTypeAdapterFactory threw IllegalStateException: Not a JSON Object: null when serializing a null undeclared-property value. The fix shipped in #24573 without test coverage; this tests-only PR closes that gap so the issue can be closed. No template changes or sample regeneration needed.

  • Generator-level test generates an additionalProperties: true model plus the client's JSON factory, compiles both against the real Gson runtime, and verifies a serialize → deserialize → serialize round trip with a null value through the generated JSON.getGson(), plus unchanged behavior for string/array/object values. Adds gson-fire and okio (pinned to 3.2.0 so its Kotlin metadata stays readable by the module's test compiler) as test-scoped dependencies; jsr305 stays transitive from guava so the main compile classpath is unaffected.
  • Sample test reproduces the issue's exact repro using the petstore AdditionalPropertiesClass.
  • Updates the sample test fingerprint record for JSONTest.java to match the modified file.

Written for commit 7bf3c79. Summary will update on new commits.

View guided diff

…erties

The isJsonNull guard in the okhttp-gson CustomTypeAdapterFactory
(pojo.mustache) was merged to master via OpenAPITools#24573 without referencing
OpenAPITools#24393 and without regression tests, which left the issue open.

Fixes OpenAPITools#24393

- generator-level test JavaOkHttpGsonNullAdditionalPropertiesTest:
  generates an additionalProperties:true model with the java/okhttp-gson
  generator, compiles the generated CustomTypeAdapterFactory against the
  real Gson runtime (same fixture approach as
  JavaOkHttpGsonOneOfPrimitiveArrayTest) and asserts a
  serialize -> deserialize -> serialize round trip with a null
  undeclared-property value, plus unchanged behavior for
  string/array/object entry values
- sample test JSONTest.testAdditionalPropertiesNullValue with the
  petstore AdditionalPropertiesClass repro from the issue

No template changes and no sample regeneration required: okhttp-gson
samples already carry the guard from OpenAPITools#24573, and a targeted regen of
bin/configs/java-okhttp-gson.yaml produces no diff.

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java">

<violation number="1" location="samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java:788">
P3: `deserialized` is populated from unrelated hard-coded JSON, so this does not exercise the claimed serialize→deserialize→serialize path. Parse the serialized output instead, and initialize `map_property` on `model` if that field must remain asserted.</violation>
</file>

Reply with feedback, questions, or to request a fix.

View guided diff | Re-trigger cubic

assertFalse(model.toJson().contains("unknownField"));

// deserialize: JSON null undeclared properties are stored as null-valued entries
AdditionalPropertiesClass deserialized = AdditionalPropertiesClass.fromJson("{\"map_property\":{\"key\":\"value\"},\"unknownField\":null}");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: deserialized is populated from unrelated hard-coded JSON, so this does not exercise the claimed serialize→deserialize→serialize path. Parse the serialized output instead, and initialize map_property on model if that field must remain asserted.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At samples/client/petstore/java/okhttp-gson/src/test/java/org/openapitools/client/JSONTest.java, line 788:

<comment>`deserialized` is populated from unrelated hard-coded JSON, so this does not exercise the claimed serialize→deserialize→serialize path. Parse the serialized output instead, and initialize `map_property` on `model` if that field must remain asserted.</comment>

<file context>
@@ -765,6 +765,39 @@ public void testAdditionalArrayProperties() throws IOException {
+        assertFalse(model.toJson().contains("unknownField"));
+
+        // deserialize: JSON null undeclared properties are stored as null-valued entries
+        AdditionalPropertiesClass deserialized = AdditionalPropertiesClass.fromJson("{\"map_property\":{\"key\":\"value\"},\"unknownField\":null}");
+        assertEquals(deserialized.getMapProperty(), Collections.singletonMap("key", "value"));
+        assertTrue(deserialized.getAdditionalProperties().containsKey("unknownField"));
</file context>

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 349bca4: the generator-side test (JavaOkHttpGsonNullAdditionalPropertiesTest) now runs the full serialize -> deserialize -> serialize round-trip through the client's configured Gson. It generates the client (model + supporting JSON factory), compiles the generated sources against the real Gson runtime (gson-fire, okio and jsr305 added as test-scoped dependencies of the generator module) and takes the Gson from the generated JSON.getGson() instead of a hand-built builder over a model stub. A model instance with a null-valued additionalProperties entry is serialized, the resulting JSON is fed back into fromJson, the null entry is asserted to survive the round trip, and re-serialization is asserted byte-stable; the existing no-throw-on-serialize assertion is kept. The generator-side test and the sample JSONTest (34 tests) both pass locally.

…t Gson

Rework JavaOkHttpGsonNullAdditionalPropertiesTest so the claimed
serialize -> deserialize -> serialize round trip is actually exercised:
the test generates the client (model + supporting JSON factory), compiles
the generated sources against the real Gson runtime (gson-fire, okio and
jsr305 added as test-scoped dependencies of the generator module) and
takes the Gson from the generated JSON.getGson() instead of a hand-built
GsonBuilder over a model stub.

The model instance with a null undeclared-property value is serialized,
the resulting JSON is deserialized back, the null entry is asserted to
survive the round trip and re-serialization is asserted byte-stable.
The existing no-throw-on-serialize assertion (the ApiClient's Gson omits
named nulls) is kept.
…ope provider

Declaring com.google.code.findbugs:jsr305 directly at test scope made
Maven mediate the transitive compile-scope jsr305 (via guava) down to
test scope, so javax.annotation.Nullable disappeared from the main
compile classpath and AbstractPythonCodegen, AbstractRustCodegen and
K6ClientCodegen failed to compile on JDK 11/17 CI jobs. The generated
okhttp-gson sources still resolve javax.annotation.Nullable from the
transitive compile-scope artifact, which also covers the test classpath.

Verified locally: modules/openapi-generator main compile + the new
JavaOkHttpGsonNullAdditionalPropertiesTest pass.

Signed-off-by: Inf1mum <vadimlazuk5@gmail.com>
@Inf1mum

Inf1mum commented Nov 10, 2026

Copy link
Copy Markdown
Contributor Author

The Build: JDK 11/17 / Build / Windows tests failures on 349bca4 were caused by this PR's own pom change, not by master: declaring com.google.code.findbugs:jsr305 directly at test scope made Maven mediate the transitive compile-scope jsr305 (brought by guava) down to test scope, so javax.annotation.Nullable left the main compile classpath — exactly the three codegen classes that import it (AbstractPythonCodegen, AbstractRustCodegen, K6ClientCodegen) failed with "package javax.annotation does not exist".

Fixed in 1ebfe98 by removing the direct jsr305 declaration (the generated okhttp-gson sources still resolve javax.annotation.Nullable from the transitive compile-scope artifact, which also covers the test classpath). Verified locally that modules/openapi-generator main compile plus the new JavaOkHttpGsonNullAdditionalPropertiesTest pass. Sample outputs are unchanged, so no test_file_list.yaml fingerprint update is needed.

…piler can read

okio 3.4.0 (added in 349bca4 as a compile-time runtime dep for the generated
okhttp-gson JSON factory) also landed on the module-wide test classpath, which
KotlinTestUtils feeds into kotlin-compiler-embeddable 1.6.21 when compiling
generated Kotlin. okio 3.4.0 carries Kotlin metadata 1.8.0 and broke
KotlinTestUtilsTest, KotlinMicroprofileClientCodegenTest and
KotlinSpringServerCodegenTest ('binary version of its metadata is 1.8.0,
expected version is 1.6.0'). okio 3.2.0 carries metadata 1.6.0 and keeps the
generated-client compile path working.

Signed-off-by: Inf1mum <vadimlazuk5@gmail.com>

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG][JAVA][gson] CustomTypeAdapterFactory throws "Not a JSON Object:null" for null additionalProperties values

1 participant