diff --git a/src/oas.md b/src/oas.md index 9151acac30..5115f73022 100644 --- a/src/oas.md +++ b/src/oas.md @@ -2475,7 +2475,7 @@ JSON Schema implementations MAY choose to treat keywords defined by the OpenAPI | Field Name | Type | Description | | ---- | :----: | ---- | | discriminator | [Discriminator Object](#discriminator-object) | The discriminator provides a "hint" for which of a set of schemas a payload is expected to satisfy. See [Composition and Inheritance](#composition-and-inheritance-polymorphism) for more details. | -| xml | [XML Object](#xml-object) | This MAY be used only on property schemas. It has no effect on root schemas. Adds additional metadata to describe the XML representation of this property. | +| xml | [XML Object](#xml-object) | Adds additional metadata to describe the XML representation of this schema. | | externalDocs | [External Documentation Object](#external-documentation-object) | Additional external documentation for this schema. | | example | Any | A free-form field to include an example of an instance for this schema. To represent examples that cannot be naturally represented in JSON or YAML, a string value can be used to contain the example with escaping where necessary.

**Deprecated:** The `example` field has been deprecated in favor of the JSON Schema `examples` keyword. Use of `example` is discouraged, and later versions of this specification may remove it. | @@ -3077,22 +3077,68 @@ will map to `#/components/schemas/Dog` because the `dog` entry in the `mapping` #### XML Object A metadata object that allows for more fine-tuned XML model definitions. - -When using arrays, XML element names are _not_ inferred (for singular/plural forms) and the `name` field SHOULD be used to add that information. -See examples for expected behavior. +When using a Schema Object with XML, if no XML Object is present, the behavior is determined by the XML Object's default field values. ##### Fixed Fields | Field Name | Type | Description | | ---- | :----: | ---- | -| name | `string` | Replaces the name of the element/attribute used for the described schema property. When defined within `items`, it will affect the name of the individual XML elements within the list. When defined alongside `type` being `"array"` (outside the `items`), it will affect the wrapping element if and only if `wrapped` is `true`. If `wrapped` is `false`, it will be ignored. | +| nodeType | `string` | One of `element`, `attribute`, `text`, `cdata`, or `none`, as explained under [XML Node Types](#xml-node-types). The default value is `none` if `$ref`, `$dynamicRef`, or `type: "array"` is present in the [Schema Object](#schema-object) containing the XML Object, and `element` otherwise. | +| name | `string` | Sets the name of the element/attribute corresponding to the schema, replacing the name that was inferred as described under [XML Node Names](#xml-node-names). This field SHALL be ignored if the `nodeType` is `text`, `cdata`, or `none`. | | namespace | `string` | The IRI ([[RFC3987]]) of the namespace definition. Value MUST be in the form of a non-relative IRI. | | prefix | `string` | The prefix to be used for the [name](#xml-name). | -| attribute | `boolean` | Declares whether the property definition translates to an attribute instead of an element. Default value is `false`. | -| wrapped | `boolean` | MAY be used only for an array definition. Signifies whether the array is wrapped (for example, ``) or unwrapped (``). Default value is `false`. The definition takes effect only when defined alongside `type` being `"array"` (outside the `items`). | +| attribute | `boolean` | Declares whether the property definition translates to an attribute instead of an element. Default value is `false`. If `nodeType` is present, this field MUST NOT be present.

**Deprecated:** Use `nodeType: "attribute"` in place of `attribute: true` | +| wrapped | `boolean` | MAY be used only for an array definition. Signifies whether the array is wrapped (for example, ``) or unwrapped (``). Default value is `false`. The definition takes effect only when defined alongside `type` being `"array"` (outside the `items`). If `nodeType` is present, this field MUST NOT be present.

**Deprecated:** Set `nodeType: "element"` explicitly in place of `wrapped: true` | + +Note that when generating an XML document from object data, the order of the nodes is undefined. +Use `prefixItems` to control node ordering as shown under [Ordered Elements and Text](#ordered-elements-and-text). + +See [Appendix B](#appendix-b-data-type-conversion) for a discussion of converting values of various types to string representations. This object MAY be extended with [Specification Extensions](#specification-extensions). +##### XML Node Types + +Each Schema Object describes a particular type of XML [[!DOM]] [node](https://dom.spec.whatwg.org/#interface-node) which is specified by the `nodeType` field, which has the following possible values. +Except for the special value `none`, these values have numeric equivalents in the DOM specification which are given in parentheses after the name: + +* `element` (1): The schema represents an element and describes its contents +* `attribute` (2): The schema represents an attribute and describes its value +* `text` (3): The schema represents a text node (parsed character data) +* `cdata` (4): The schema represents a CDATA section +* `none`: The schema does not correspond to any node in the XML document, and the nodes corresponding to its subschema(s) are included directly under its parent schema's node + +The `none` type is useful for JSON Schema constructs that require more Schema Objects than XML nodes, such as a schema containing only `$ref` that exists to facilitate re-use rather than imply any structure. + +###### Modeling Element Lists + +For historical compatibility, schemas of `type: "array"` default to `nodeType: "none"`, placing the nodes for each array item directly under the parent node. +This also aligns with the inferred naming behavior defined under [XML Node Names](#xml-node-names). + +To produce an element wrapping the list, set an explicit `nodeType: "element"` on the `type: "array"` schema. +When doing so, it is advisable to set an explicit name on either the wrapping element or the item elements to avoid them having the same inferred name. +See examples for expected behavior. + +###### Implicit and Explicit `text` Nodes + +If an `element` node has a primitive type, then the schema also produces an implicit `text` node described by the schema for the contents of the `element` node named by the property name (or `name` field). + +Explicit `text` nodes are necessary if an element has both attributes and content. + +Note that placing two `text` nodes adjacent to each other is ambiguous for parsing, and the resulting behavior is implementation-defined. + +##### XML Node Names + +The `element` and `attribute` node types require a name, which MUST be inferred from the schema as follows, unless overridden by the `name` field: + +* For schemas directly under the [Components Object's](#components-object) `schemas` field, the component name is the inferred name. +* For property schemas, and for array item schemas under a property schema, the property name is the inferred name +* In all other cases, such as an inline schema under a [Media Type Object's](#media-type-object) `schema` field, no name can be inferred and an XML Object with a `name` field MUST be present + +Note that when using arrays, singular vs plural forms are _not_ inferred, and must be set explicitly. + +##### Namespace Limitations + The `namespace` field is intended to match the syntax of [XML namespaces](https://www.w3.org/TR/xml-names11/), although there are a few caveats: * Versions 3.1.0, 3.0.3, and earlier of this specification erroneously used the term "absolute URI" instead of "non-relative URI" ("non-relative IRI" as of OAS v3.2.0), so authors using namespaces that include a fragment should check tooling support carefully. @@ -3100,29 +3146,31 @@ The `namespace` field is intended to match the syntax of [XML namespaces](https: ##### XML Object Examples -Each of the following examples represent the value of the `properties` keyword in a [Schema Object](#schema-object) that is omitted for brevity. -The JSON and YAML representations of the `properties` value are followed by an example XML representation produced for the single property shown. +The Schema Objects are followed by an example XML representation produced for the schema shown. +For examples using `attribute` or `wrapped`, please see version 3.1 of the OpenAPI Specification. -###### No XML Element +###### No XML Object -Basic string property: +Basic string property (`nodeType` is `element` by default): ```yaml -animals: - type: string +properties: + animals: + type: string ``` ```xml ... ``` -Basic string array property ([`wrapped`](#xml-wrapped) is `false` by default): +Basic string array property (`nodeType` is `none` by default): ```yaml -animals: - type: array - items: - type: string +properties: + animals: + type: array + items: + type: string ``` ```xml @@ -3134,10 +3182,11 @@ animals: ###### XML Name Replacement ```yaml -animals: - type: string - xml: - name: animal +properties: + animals: + type: string + xml: + name: animal ``` ```xml @@ -3146,22 +3195,24 @@ animals: ###### XML Attribute, Prefix and Namespace -In this example, a full model definition is shown. +Note that the name of the root XML element comes from the component name. ```yaml -Person: - type: object - properties: - id: - type: integer - format: int32 - xml: - attribute: true - name: - type: string - xml: - namespace: https://example.com/schema/sample - prefix: sample +components: + schemas: + Person: + type: object + properties: + id: + type: integer + format: int32 + xml: + attribute: true + name: + type: string + xml: + namespace: https://example.com/schema/sample + prefix: sample ``` ```xml @@ -3175,12 +3226,13 @@ Person: Changing the element names: ```yaml -animals: - type: array - items: - type: string - xml: - name: animal +properties: + animals: + type: array + items: + type: string + xml: + name: animal ``` ```xml @@ -3188,17 +3240,18 @@ animals: value ``` -The external `name` field has no effect on the XML: +The `name` field for the `type: "array"` schema has no effect because the default `nodeType` for that object is `none`: ```yaml -animals: - type: array - items: - type: string +properties: + animals: + type: array + items: + type: string + xml: + name: animal xml: - name: animal - xml: - name: aliens + name: aliens ``` ```xml @@ -3206,15 +3259,16 @@ animals: value ``` -Even when the array is wrapped, if a name is not explicitly defined, the same name will be used both internally and externally: +Even when a wrapping element is explicitly created by setting `nodeType` to `element`, if a name is not explicitly defined, the same name will be used for both the wrapping element and the list item elements: ```yaml -animals: - type: array - items: - type: string - xml: - wrapped: true +properties: + animals: + type: array + items: + type: string + xml: + nodeType: element ``` ```xml @@ -3227,14 +3281,15 @@ animals: To overcome the naming problem in the example above, the following definition can be used: ```yaml -animals: - type: array - items: - type: string +properties: + animals: + type: array + items: + type: string + xml: + name: animal xml: - name: animal - xml: - wrapped: true + nodeType: element ``` ```xml @@ -3244,18 +3299,19 @@ animals: ``` -Affecting both internal and external names: +Affecting both wrapping element and item element names: ```yaml -animals: - type: array - items: - type: string +properties: + animals: + type: array + items: + type: string + xml: + name: animal xml: - name: animal - xml: - name: aliens - wrapped: true + name: aliens + nodeType: element ``` ```xml @@ -3265,16 +3321,17 @@ animals: ``` -If we change the external element but not the internal ones: +If we change the wrapping element name but not the item element names: ```yaml -animals: - type: array - items: - type: string - xml: - name: aliens - wrapped: true +properties: + animals: + type: array + items: + type: string + xml: + name: aliens + nodeType: element ``` ```xml @@ -3284,6 +3341,229 @@ animals: ``` +###### Elements With Attributes And Text + +```yaml +properties: + animals: + type: array + xml: + nodeType: element + name: animals + items: + properties: + kind: + type: string + xml: + nodeType: attribute + name: animal + content: + type: string + xml: + nodeType: text +``` + +```xml + + Fluffy + Fido + +``` + +###### Referenced Element With CDATA + +In this example, no element is created for the Schema Object that contains only the `$ref`, as its `nodeType` defaults to `none`. +It is necessary to create a subschema for the CDATA section as otherwise the content would be treated as an implicit node of type `text`. + +```yaml +paths: + /docs: + get: + responses: + "200": + content: + application/xml: + $ref: "#/components/schemas/Documentation" +components: + schemas: + Documentation: + type: object + properties: + content: + type: string + contentMediaType: text/html + xml: + nodeType: cdata +``` + +```xml + + Awesome Docs]]> + +``` + +Alternatively, the named root element could be set at the point of use and the root element disabled on the component: + +```yaml +paths: + /docs: + get: + responses: + "200": + content: + application/xml: + xml: + nodeType: element + name: StoredDocument + $ref: "#/components/schemas/Documentation" + put: + requestBody: + required: true + content: + application/xml: + xml: + nodeType: element + name: UpdatedDocument + $ref: "#/components/schemas/Documentation" + responses: + "201": {} +components: + schemas: + Documentation: + xml: + nodeType: none + type: object + properties: + content: + type: string + contentMediaType: text/html + xml: + nodeType: cdata +``` + +The GET response XML: + +```xml + + Awesome Docs]]> + +``` + +The PUT request XML: + +```xml + + Awesome Docs]]> + +``` + +The in-memory instance data for all three of the above XML documents: + +```json +{ + "content": "Awesome Docs" +} +``` + +###### Ordered Elements and Text + +To control the exact order of elements, use the `prefixItems` keyword. +With this approach, it is necessary to set the element names using the XML Object as they would otherwise all inherit the parent's name despite being different elements in a specific order. +It is also necessary to set `nodeType: "element"` explicitly on the array in order to get an element containing the sequence. + +This first ordered example shows a sequence of elements, as well as the recommended serialization of `null` for elements: + +```yaml +components: + schemas: + OneTwoThree: + xml: + nodeType: element + type: array + minLength: 3 + maxLength: 3 + prefixItems: + - xml: + name: One + type: string + - xml: + name: Two + type: object + required: + - unit + - value + properties: + unit: + type: string + xml: + nodeType: attribute + value: + type: number + xml: + nodeType: text + - xml: + name: Three + type: + - boolean + - "null" +``` + +```xml + + Some text + 42 + + +``` + +The in-memory instance data that would produce the above XML snippet with the preceding schema would be: + +```json +[ + "Some Text", + { + "unit": "cubits", + "value": 42 + }, + null +] +``` + +In this next example, the `name` needs to be set for the element, while the `nodeType` needs to be set for the text nodes. + +```yaml +components: + schemas: + Report: + xml: + nodeType: element + type: array + prefixItems: + - xml: + nodeType: text + type: string + - xml: + name: data + type: number + - xml: + nodeType: text + type: string +``` + +```xml +Some preamble text.42Some postamble text. +``` + +The in-memory instance data structure for the above example would be: + +```json +[ + "Some preamble text." + 42, + "Some postamble text." +] +``` + #### Security Scheme Object Defines a security scheme that can be used by the operations. diff --git a/src/schemas/validation/meta.yaml b/src/schemas/validation/meta.yaml index 491190a221..52f5ea2ed0 100644 --- a/src/schemas/validation/meta.yaml +++ b/src/schemas/validation/meta.yaml @@ -55,8 +55,14 @@ $defs: xml: $ref: '#/$defs/extensible' properties: - attribute: - type: boolean + nodeType: + type: string + enum: + - element + - attribute + - text + - cdata + - none name: type: string namespace: @@ -64,7 +70,14 @@ $defs: type: string prefix: type: string + attribute: + type: boolean wrapped: type: boolean type: object + dependentSchemas: + nodeType: + properties: + attribute: false + wrapped: false unevaluatedProperties: false