Skip to content

Latest commit

 

History

History
142 lines (129 loc) · 5.21 KB

SOAP.md

File metadata and controls

142 lines (129 loc) · 5.21 KB

Example Soap tags

This example is based upon bug #42

Output

<?xml version="1.0" ?>
<S:Envelope xmlns:S="http://schemas.xmlsoap.org/soap/envelope/">
    <S:Body>
        <ns2:Ge xmlns:ns2="http://www.gxtlink.com/webservice/">
            <code>0</code>
            <data>
                <project>get</project>
                <unit>p</unit>
            </data>
        </ns2:Ge>
    </S:Body>
</S:Envelope>

Data types

Envelope & Body - Envelope.kt

/**
 * The Envelope class is a very simple implementation of the SOAP Envelope (ignoring existence of headers). The
 * `@XmlSerialName` annotation specifies how the class is to be serialized, including namespace and prefix to try to use
 * (the serializer will try to reuse an existing prefix for the namespace if it already exists in the document).
 *
 * @property body `body` is a property that contains the body of the envelope. It merely wraps the data, but needs to exist
 *      for the purpose of generating the tag.
 * @param BODYTYPE SOAP is a generic protocol and the wrappers should not depend on a particular body data. That is why
 *      the type is parameterized (this works fine with Serialization).
 */
@Serializable
@XmlSerialName("Envelope", "http://schemas.xmlsoap.org/soap/envelope/", "S")
class Envelope<BODYTYPE> private constructor(
    private val body: Body<BODYTYPE>
                                            ) {

    /**
     * Actual constructor so users don't need to know about the body element
     */
    constructor(data: BODYTYPE) : this(Body(data))

    /**
     * Accessor to the data property that hides the body element.
     */
    val data: BODYTYPE get() = body.data

    override fun toString(): String {
        return "Envelope(body=$body)"
    }

    /**
     * The body class merely wraps a data element (the SOAP standard requires this to be a single element). There is no
     * need for this type to specify the serial name explicitly because:
     *  1. Body is a class, thus serialized as element. The name used is therefore (by default) determined by the name
     *     of the type (`Body`).
     *  2. The namespace (and prefix) used for a type is by default the namespace of the containing tag.
     *  3. Package names are normally elided in the naming
     *
     * The content of data is polymorphic to allow for different message types.
     *
     * @property data The data property contains the actual message content for the soap message.
     */
    @Serializable
    private data class Body<BODYTYPE>(@Polymorphic val data: BODYTYPE)

}

GeResult - GeResult.kt:

/**
 * This class represents an actual message in the gtxlink webservice. It carries its own namespace and has a predefined
 * prefix. There needs to be a prefix here as the content in the example uses the unnamed namespace.
 */
@Serializable
@XmlSerialName("Ge", "http://www.gxtlink.com/webservice/", "ns2")
data class GeResult<out T>(
    /**
     * Code is a primitive, so the name comes from here (the use site). The example data has this in the empty namespace
     * so we must specify this namespace to avoid the default (inheriting the namespace). We want to write this as
     * element so need to specify the `@XmlElement` annotation.
     */
    @XmlSerialName("code", "", "")
    @XmlElement(true)
    val code: Int,
    /**
     * The data property does not require annotation. It is not a primitive so by default the name comes from the actual
     * serialized type and is serialized as element (by default).
     */
    val data: T
                      )

GeResultData - GeResultData.kt:

/**
 * This class represents the actual data payload of the message. The name needs to be specified, as well as the namespace
 * (and prefix).
 */
@Serializable
@XmlSerialName("data", "", "")
data class GeResultData(
    @XmlElement(true)
    val project: String,
    @XmlElement(true)
    val unit: String
                       ) 

Example usage

main.kt

/**
 * This is a simple example representing issue #42 on the parsing of soap messages. Note that it doesn't address
 * the idea of making
 */
fun main() {
    val data = Envelope(GeResult(0, GeResultData("get", "p")))
    val module = SerializersModule {
        polymorphic(Any::class) {
            subclass(GeResult::class as KClass<GeResult<GeResultData>>, serializer())
        }
    }
    val xml = XML(module) {
        indentString = "    "
        xmlDeclMode = XmlDeclMode.Minimal
        autoPolymorphic = true
    }

    val serializer = serializer<Envelope<GeResult<GeResultData>>>()
    println("SOAP descriptor:\n${xml.xmlDescriptor(serializer).toString().prependIndent("    ")}")

    val encodedString = xml.encodeToString(/*serializer, */data) // both versions are available
    println("SOAP output:\n${encodedString.prependIndent("    ")}\n")

    // the inline reified version is is also available
    val reparsedData = xml.decodeFromString(serializer, encodedString)
    println("SOAP input: $reparsedData")
}