Skip to content

Support for XML request body and response body #556

Open
@ugocottin

Description

@ugocottin

Motivation

I want to be able de generate client and server from an OpenAPI specification, for an API which use XML as content type for request and response body.

Currently, application/xml contents for request and response are not generated by swift-openapi-generator, and only OpenAPIRuntime.HTTPBody enum associated value is available for xml content.

A Workaround is to use an external XML encoder and decoder, like CoreOffice XMLCoder, and make the encoding from / decoding to HTTPBody.

struct Credentials: Encodable {
    var username: String
    var password_hash: String
}

struct Token: Decodable {
    var token: String
}
let credentials = Credentials(username: "johnapplessed", password_hash: "d2hhdCBkaWQgeW91IGV4cGVjdD8=")
let encoder = XMLEncoder()
let encoded = try encoder.encode(credentials)
let response = try await client.authenticate(body: .xml(HTTPBody(encoded)))
let xmlBody = try response.ok.body.xml
let xmlData = try await Data(collecting: xmlBody, upTo: .max)
let decoder = XMLDecoder()
let decoded = try decoder.decode(Token.self, from: xmlData)
let token = decoded.token

Proposed solution

One quick solution could be to implement a new CodingStrategy for xml in swift-openapi-generator.
For swift-openapi-runtime, we could add the following methods to Converter:

Client/server Set/get Schema location Coding strategy Optional/required Method name
client set request body XML optional setOptionalRequestBodyAsXML
client set request body XML required setRequiredRequestBodyAsXML
client get response body XML required getResponseBodyAsXML
server get request body XML optional getOptionalRequestBodyAsXML
server get request body XML required getRequiredRequestBodyAsXML
server set response body XML required setResponseBodyAsXML

Encoding / Decoding logic can be implemented into these methods, with an XML encoder / decoder.
As Converter have encoder and decoder properties as type JSONEncoder and JSONDecoder, we could:

  1. Rename encoder to jsonEncoder and decoder to jsonDecoder, and instantiate a xmlEncoder and xmlDecoder in Converter init.
  2. Instantiate a XMLEncoder or XMLDecoder in call of methods above.

Example:

extension Converter {
    public func setRequiredRequestBodyAsXML<T: Encodable>(
        _ value: T,
        headerFields: inout HTTPFields,
        contentType: String
    ) throws -> HTTPBody {
        try setRequiredRequestBody(
            value,
            headerFields: &headerFields,
            contentType: contentType,
            convert: convertBodyCodableToXML
        )
    }
}

Alternatives considered

I found that Converter struct is not suited for adding support of new content type. With the proposed solution, the Converter struct will have types of <#HTTPMediaType#>Encoder and <#HTTPMediaType#>Decoder, while not necessarily needed.
For XML only API, Converter still need a JSONEncoder and JSONDecoder.

I thought that, maybe, and I would like to have your opinion on this, we must implement a Converter for a specific http media type, like Vapor is doing with ContentEncoder and ContentDecoder. Each http media type would be assigned to a specific Converter.

What's your thoughts about this?

Additional information

No response

Metadata

Metadata

Assignees

Labels

area/generatorAffects: plugin, CLI, config file.area/runtimeAffects: the runtime library.kind/featureNew feature.size/MMedium task. (A couple of days of work.)

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions