Repository: swaggest/openapi-go
Pull request #99 (2024-03-05 02:22:19)

Add JSON collectionFormat

github-actions[bot]: ### Lines Of Code

| Language  | Files |   Lines    |    Code    | Comments |  Blanks  | Complexity |     Bytes      |
|-----------|-------|------------|------------|----------|----------|------------|----------------|
| Go        | 21    | 3956 (+3)  | 2947 (+2)  | 214      | 795 (+1) | 803 (+3)   | 90.8K (+49B)   |
| Go (test) | 21    | 5841 (+24) | 4395 (+24) | 660      | 786      | 59         | 152.2K (+549B) |
| Markdown  | 4     | 234 (+1)   | 205 (+1)   | 0        | 29       | 0          | 6.7K (+65B)    |
<!-- Sticky Pull Request CommentLOC -->
github-actions[bot]: ### Go API Changes

<pre>
# summary
Inferred base version: v0.2.46
Suggested version: v0.2.47
</pre>
<!-- Sticky Pull Request Commentgorelease -->
github-actions[bot]: ### Unit Test Coverage
total:									(statements)				43.0%
changed lines: (statements) 100.0%
<details><summary>Coverage of changed lines</summary>

|           File           |     Function      | Coverage |
|--------------------------|-------------------|----------|
| Total                    |                   | 100.0%   |
| openapi3/reflect.go      |                   | 100.0%   |
| openapi3/reflect.go:409  | parseParametersIn | 100.0%   |
| openapi31/reflect.go     |                   | 100.0%   |
| openapi31/reflect.go:361 | parseParametersIn | 100.0%   |

</details>

<details><summary>Coverage diff with base branch</summary>

No changes in coverage.

</details>
<!-- Sticky Pull Request Commentunit-test -->

Patch:
diff --git a/README.md b/README.md
index b540b07..2ea8511 100644
--- a/README.md
+++ b/README.md
@@ -27,7 +27,8 @@ For automated HTTP REST service framework built with this library please check [
         * `csv` comma-separated values,
         * `ssv` space-separated values,
         * `pipes` pipe-separated values (`|`),
-        * `multi` ampersand-separated values (`&`), 
+        * `multi` ampersand-separated values (`&`),
+        * `json` additionally to slices unpacks maps and structs,
 * Flexible schema control with [`jsonschema-go`](https://github.com/swaggest/jsonschema-go#implementing-interfaces-on-a-type)
 
 ## Example
diff --git a/openapi3/reflect.go b/openapi3/reflect.go
index 8f01a90..afae59e 100644
--- a/openapi3/reflect.go
+++ b/openapi3/reflect.go
@@ -447,10 +447,10 @@ func (r *Reflector) parseParametersIn(
 				Content:     nil,
 			}
 
-			swg2CollectionFormat := ""
-			refl.ReadStringTag(field.Tag, "collectionFormat", &swg2CollectionFormat)
+			collectionFormat := ""
+			refl.ReadStringTag(field.Tag, "collectionFormat", &collectionFormat)
 
-			switch swg2CollectionFormat {
+			switch collectionFormat {
 			case "csv":
 				p.WithStyle(string(QueryParameterStyleForm)).WithExplode(false)
 			case "ssv":
@@ -463,7 +463,9 @@ func (r *Reflector) parseParametersIn(
 
 			// Check if parameter is an JSON encoded object.
 			property := reflect.New(field.Type).Interface()
-			if refl.HasTaggedFields(property, tagJSON) && !refl.HasTaggedFields(property, string(in)) {
+
+			if collectionFormat == "json" ||
+				(refl.HasTaggedFields(property, tagJSON) && !refl.HasTaggedFields(property, string(in))) {
 				propertySchema, err := r.Reflect(property,
 					openapi.WithOperationCtx(oc, false, in),
 					jsonschema.DefinitionsPrefix(componentsSchemas),
diff --git a/openapi31/reflect.go b/openapi31/reflect.go
index 923dc2e..be9ac64 100644
--- a/openapi31/reflect.go
+++ b/openapi31/reflect.go
@@ -392,10 +392,10 @@ func (r *Reflector) parseParametersIn(
 				Content:     nil,
 			}
 
-			swg2CollectionFormat := ""
-			refl.ReadStringTag(field.Tag, "collectionFormat", &swg2CollectionFormat)
+			collectionFormat := ""
+			refl.ReadStringTag(field.Tag, "collectionFormat", &collectionFormat)
 
-			switch swg2CollectionFormat {
+			switch collectionFormat {
 			case "csv":
 				p.WithStyle(ParameterStyleForm).WithExplode(false)
 			case "ssv":
@@ -408,7 +408,8 @@ func (r *Reflector) parseParametersIn(
 
 			// Check if parameter is an JSON encoded object.
 			property := reflect.New(field.Type).Interface()
-			if refl.HasTaggedFields(property, tagJSON) && !refl.HasTaggedFields(property, string(in)) { //nolint:nestif
+			if collectionFormat == "json" || //nolint:nestif
+				(refl.HasTaggedFields(property, tagJSON) && !refl.HasTaggedFields(property, string(in))) {
 				propertySchema, err := r.Reflect(property,
 					openapi.WithOperationCtx(oc, false, in),
 					jsonschema.DefinitionsPrefix(componentsSchemas),


Test patch:
diff --git a/openapi3/reflect_test.go b/openapi3/reflect_test.go
index 0e6c37e..6d0dc1e 100644
--- a/openapi3/reflect_test.go
+++ b/openapi3/reflect_test.go
@@ -554,6 +554,7 @@ func TestReflector_AddOperation_request_jsonQuery(t *testing.T) {
 		One   filter         `query:"one"`
 		Two   filter         `query:"two"`
 		Three map[string]int `query:"three"`
+		Four  map[string]int `query:"four" collectionFormat:"json"`
 	}
 
 	r := openapi3.Reflector{}
@@ -585,6 +586,17 @@ func TestReflector_AddOperation_request_jsonQuery(t *testing.T) {
 			  {
 				"name":"three","in":"query","style":"deepObject","explode":true,
 				"schema":{"type":"object","additionalProperties":{"type":"integer"}}
+			  },
+			  {
+				"name":"four","in":"query",
+				"content":{
+				  "application/json":{
+					"schema":{
+					  "type":"object","additionalProperties":{"type":"integer"},
+					  "nullable":true
+					}
+				  }
+				}
 			  }
 			],
 			"responses":{"204":{"description":"No Content"}}
diff --git a/openapi31/reflect_test.go b/openapi31/reflect_test.go
index b5352ee..ffc9354 100644
--- a/openapi31/reflect_test.go
+++ b/openapi31/reflect_test.go
@@ -574,6 +574,7 @@ func TestReflector_AddOperation_request_jsonQuery(t *testing.T) {
 		One   filter         `query:"one"`
 		Two   filter         `query:"two"`
 		Three map[string]int `query:"three"`
+		Four  map[string]int `query:"four" collectionFormat:"json"`
 	}
 
 	r := openapi31.Reflector{}
@@ -609,6 +610,17 @@ func TestReflector_AddOperation_request_jsonQuery(t *testing.T) {
 				  "type":["object","null"]
 				},
 				"style":"deepObject","explode":true
+			  },
+			  {
+				"name":"four","in":"query",
+				"content":{
+				  "application/json":{
+					"schema":{
+					  "additionalProperties":{"type":"integer"},
+					  "type":["null","object"]
+					}
+				  }
+				}
 			  }
 			],
 			"responses":{"204":{"description":"No Content"}}