-
Notifications
You must be signed in to change notification settings - Fork 539
Description
Description
When parsing an OpenAPI 3.1 specification with ParseOptions.setResolve(true), the OpenAPIDereferencer31 unexpectedly merges identical inline objects (schemas, responses, etc.) into the same object reference. This behavior is inconsistent with the OpenAPI specification and causes issues for code generation tools.
Affected Version
2.1.37
Steps to Reproduce
Test OpenAPI 3.1 Spec (swagger.json):
{
"openapi": "3.1.0",
"info": {
"title": "API",
"version": "1.0"
},
"paths": {
"/path1": {
"post": {
"responses": {
"200": {
"description": "result",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": { "type": "string" },
"status": { "type": "string" }
}
}
}
}
}
}
}
},
"/path2": {
"post": {
"responses": {
"200": {
"description": "result",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"message": { "type": "string" },
"status": { "type": "string" }
}
}
}
}
}
}
}
}
}
}Test Code:
// With resolve=true
ParseOptions options1 = new ParseOptions();
options1.setResolve(true);
SwaggerParseResult result1 = new OpenAPIParser().readLocation("swagger.json", null, options1);
ApiResponse path1Response = result1.getOpenAPI().getPaths().get("/path1").getPost().getResponses().get("200");
ApiResponse path2Response = result1.getOpenAPI().getPaths().get("/path2").getPost().getResponses().get("200");
System.out.println(path1Response == path2Response); // Prints: true (UNEXPECTED!)
// With resolve=false
ParseOptions options2 = new ParseOptions();
options2.setResolve(false);
SwaggerParseResult result2 = new OpenAPIParser().readLocation("swagger.json", null, options2);
ApiResponse path1Response2 = result2.getOpenAPI().getPaths().get("/path1").getPost().getResponses().get("200");
ApiResponse path2Response2 = result2.getOpenAPI().getPaths().get("/path2").getPost().getResponses().get("200");
System.out.println(path1Response2 == path2Response2); // Prints: false (EXPECTED)In OpenAPI31Traverser.java, the traversal logic uses:
if (visitedMap.containsKey(mediaType)) {
return (MediaType)visitedMap.get(mediaType); // Returns cached object
}Since MediaType, Schema, ApiResponse, and other model classes override equals() to compare content rather than object identity, the visitedMap incorrectly identifies different inline objects as the same when their content matches.
Expected Behavior
According to the OpenAPI 3.1 specification, two inline objects with identical content should remain as separate, independent objects unless they are explicitly referenced using $ref. Each inline definition should maintain its own object identity.
Actual Behavior
When resolve=true is enabled, the OpenAPI31Traverser uses a visitedMap (HashMap) that relies on the equals() method to cache visited objects. When it encounters a second inline object with identical content, it returns the cached reference from the first object instead of treating them as separate entities.
Additional Context
This issue only affects OpenAPI 3.1 parsing with resolve=true. The older OpenAPI 3.0 resolver (OpenAPIResolver) does not exhibit this behavior. The issue was introduced with the new OpenAPIDereferencer31 implementation.
The DISABLE_OAS31_RESOLVE environment variable can be used as a workaround to disable OAS 3.1 resolution, but this prevents proper $ref resolution as well.
Checklist
- I have searched the existing issues and this is not a duplicate.
- I have provided sufficient information for maintainers to reproduce the issue.