Repository: swagger-api/swagger-parser
Pull request #2181 (2025-05-14 12:22:25)

Add $dynamicRef
NOTE: this PR requires swagger-core 2.2.32 to be released first


Patch:
diff --git a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
index c8af1f90..c6d6d3ca 100644
--- a/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
+++ b/modules/swagger-parser-v3/src/main/java/io/swagger/v3/parser/util/OpenAPIDeserializer.java
@@ -173,7 +173,8 @@ public class OpenAPIDeserializer {
             "default", "discriminator", "readOnly", "writeOnly", "xml", "externalDocs", "example", "deprecated",
 			"const", "examples", "$id", "$comment", "if", "then", "else", "unevaluatedProperties","unevaluatedItems", "prefixItems",
             "contains","contentEncoding","contentMediaType","$anchor","$schema","contentSchema","propertyNames",
-            "dependentSchemas","dependentRequired","minContains","maxContains","patternProperties", "$vocabulary", "$dynamicAnchor"));
+            "dependentSchemas","dependentRequired","minContains","maxContains","patternProperties", "$vocabulary",
+            "$dynamicAnchor", "$dynamicRef"));
 	protected static Set<String> EXAMPLE_KEYS_31 = new LinkedHashSet<>(Arrays.asList("$ref", "summary", "description",
 			"value", "externalValue"));
 	protected static Set<String> HEADER_KEYS_31 = new LinkedHashSet<>(Arrays.asList("$ref", "name", "in", "description",
@@ -4120,9 +4121,7 @@ public class OpenAPIDeserializer {
 							dependentRequired.add(n.textValue());
 						}
 					}
-					if (dependentRequired != null) {
-						dependentRequiredList.put(name, dependentRequired);
-					}
+                    dependentRequiredList.put(name, dependentRequired);
 				}
 			}
 		}
@@ -4143,14 +4142,12 @@ public class OpenAPIDeserializer {
                     dependentSchemasList.put(name, dependentSchemas);
                 }
             }
-            if (dependentSchemasObj != null) {
-                schema.setDependentSchemas(dependentSchemasList);
-            }
+            schema.setDependentSchemas(dependentSchemasList);
         }
 
 		//prefixItems
 		ArrayNode prefixItemsArray = getArray("prefixItems", node, false, location, result);
-		if(prefixItemsArray != null) {
+		if (prefixItemsArray != null) {
 			Schema prefixItems = new JsonSchema();
 
 			List<Schema> prefixItemsList = new ArrayList<>();
@@ -4180,11 +4177,9 @@ public class OpenAPIDeserializer {
 		Set<String> keys = getKeys(propertiesObj);
 		for (String name : keys) {
 			JsonNode propertyValue = propertiesObj.get(name);
-            if (propertiesObj != null) {
-                property = getJsonSchema(propertyValue, location, result);
-                if (property != null) {
-                    properties.put(name, property);
-                }
+            property = getJsonSchema(propertyValue, location, result);
+            if (property != null) {
+                properties.put(name, property);
             }
 		}
 		if (propertiesObj != null) {
@@ -4250,6 +4245,11 @@ public class OpenAPIDeserializer {
             schema.set$dynamicAnchor(value);
         }
 
+        value = getString("$dynamicRef", node, false, location, result);
+        if (value != null) {
+            schema.set$dynamicRef(value);
+        }
+
 		value = getString("$id", node, false, location, result);
 		if (value != null) {
 			schema.set$id(value);
diff --git a/modules/swagger-parser/pom.xml b/modules/swagger-parser/pom.xml
index 38441ae2..2235ed05 100644
--- a/modules/swagger-parser/pom.xml
+++ b/modules/swagger-parser/pom.xml
@@ -32,12 +32,6 @@
             <version>${jmockit-version}</version>
             <scope>test</scope>
         </dependency>
-        <dependency>
-            <groupId>junit</groupId>
-            <artifactId>junit</artifactId>
-            <version>${junit-version}</version>
-            <scope>test</scope>
-        </dependency>
         <dependency>
             <groupId>commons-io</groupId>
             <artifactId>commons-io</artifactId>
diff --git a/pom.xml b/pom.xml
index ddf38c03..8a83c770 100644
--- a/pom.xml
+++ b/pom.xml
@@ -416,7 +416,7 @@
         <swagger-parser-v2-version>1.0.73</swagger-parser-v2-version>
         <commons-io-version>2.18.0</commons-io-version>
         <slf4j-version>2.0.9</slf4j-version>
-        <swagger-core-version>2.2.29</swagger-core-version>
+        <swagger-core-version>2.2.32</swagger-core-version>
         <swagger-core-v2-version>1.6.15</swagger-core-v2-version>
         <junit-version>4.13.2</junit-version>
         <testng-version>7.11.0</testng-version>


Test patch:
diff --git a/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserDynamicRefTest.java b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserDynamicRefTest.java
new file mode 100644
index 00000000..d62da22a
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/java/io/swagger/v3/parser/test/OpenAPIV3ParserDynamicRefTest.java
@@ -0,0 +1,38 @@
+package io.swagger.v3.parser.test;
+
+import io.swagger.v3.oas.models.media.Schema;
+import io.swagger.v3.parser.OpenAPIV3Parser;
+import io.swagger.v3.parser.core.models.SwaggerParseResult;
+import org.testng.annotations.Test;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertNotNull;
+
+public class OpenAPIV3ParserDynamicRefTest {
+    @Test
+    public void testDynamicRefParsing() {
+        SwaggerParseResult result = new OpenAPIV3Parser()
+                .readLocation("dynamicRef/dynamicref-example.yaml", null, null);
+
+        assertNotNull(result.getOpenAPI(), "Parsed OpenAPI object should not be null");
+
+        Schema<?> rootSchema = result.getOpenAPI()
+                .getPaths().get("/tree").getGet()
+                .getResponses().get("200").getContent()
+                .get("application/json").getSchema();
+
+        assertEquals(rootSchema.get$ref(), "#/components/schemas/Node",
+                "Expected root schema to be a $ref to Node");
+
+        Schema<?> nodeSchema = result.getOpenAPI().getComponents()
+                .getSchemas().get("Node");
+
+        assertNotNull(nodeSchema, "Node schema should be parsed");
+
+        Schema<?> childrenSchema = (Schema<?>) nodeSchema.getProperties().get("children");
+        Schema<?> itemsSchema = childrenSchema.getItems();
+
+        // THIS is the actual test: you should have a get$dynamicRef() field
+        assertEquals("#node", itemsSchema.get$dynamicRef(), "Expected $dynamicRef to be preserved");
+    }
+}
diff --git a/modules/swagger-parser-v3/src/test/resources/dynamicRef/dynamicref-example.yaml b/modules/swagger-parser-v3/src/test/resources/dynamicRef/dynamicref-example.yaml
new file mode 100644
index 00000000..9e3a5c62
--- /dev/null
+++ b/modules/swagger-parser-v3/src/test/resources/dynamicRef/dynamicref-example.yaml
@@ -0,0 +1,28 @@
+openapi: 3.1.0
+info:
+  title: Test DynamicRef
+  version: 1.0.0
+paths:
+  /tree:
+    get:
+      responses:
+        '200':
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Node"
+
+components:
+  schemas:
+    Node:
+      $id: "https://example.com/schemas/node"
+      $dynamicAnchor: "node"
+      type: object
+      properties:
+        name:
+          type: string
+        children:
+          type: array
+          items:
+            $dynamicRef: "#node"
diff --git a/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java b/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
index c38c79a2..ada569d2 100644
--- a/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
+++ b/modules/swagger-parser/src/test/java/io/swagger/parser/OpenAPIParserTest.java
@@ -17,14 +17,13 @@ import io.swagger.v3.parser.core.models.SwaggerParseResult;
 import io.swagger.v3.core.util.Json;
 import java.math.BigDecimal;
 import java.math.MathContext;
-import org.junit.Test;
+
+import org.testng.annotations.Test;
 import org.testng.Assert;
 
 import java.util.Map;
-
 import java.util.List;
 
-
 import static org.testng.Assert.assertEquals;
 import static org.testng.Assert.assertNotNull;
 import static org.testng.Assert.assertTrue;
@@ -772,5 +771,6 @@ public class OpenAPIParserTest {
                 "  x-original-swagger-version: \"2.0\"\n" +
                 "openapi31: false\n");
     }
+
 }
 
diff --git a/modules/swagger-parser/src/test/resources/dynamicRef/dynamicref-example.yaml b/modules/swagger-parser/src/test/resources/dynamicRef/dynamicref-example.yaml
new file mode 100644
index 00000000..9e3a5c62
--- /dev/null
+++ b/modules/swagger-parser/src/test/resources/dynamicRef/dynamicref-example.yaml
@@ -0,0 +1,28 @@
+openapi: 3.1.0
+info:
+  title: Test DynamicRef
+  version: 1.0.0
+paths:
+  /tree:
+    get:
+      responses:
+        '200':
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: "#/components/schemas/Node"
+
+components:
+  schemas:
+    Node:
+      $id: "https://example.com/schemas/node"
+      $dynamicAnchor: "node"
+      type: object
+      properties:
+        name:
+          type: string
+        children:
+          type: array
+          items:
+            $dynamicRef: "#node"