1

I'm trying to use SimpleXmlConverterFactory with Retrofit to create an XML request to a REST service. However, the service requires the DTD declaration in the request like so.

<!DOCTYPE paymentService PUBLIC "-//WorldPay//DTD WorldPay PaymentService v1//EN" "http://dtd.worldpay.com/paymentService_v1.dtd">

But when SimpleXmlConverterFactory serializes the data objects, it leaves off the DOCTYPE declaration:

<paymentService merchantCode="..." version="1.4">
  <inquiry>
    <shopperTokenRetrieval>
      <authenticatedShopperID>...</authenticatedShopperID>
    </shopperTokenRetrieval>
  </inquiry>
</paymentService>

Creating the SimpleXmlConverterFactory isn't anything special:

val builder = Retrofit
                .Builder()
                .addConverterFactory(
                        SimpleXmlConverterFactory.create()
                )
                .baseUrl(BuildConfig.WORLDPAY_BASE_URL)
                .build()
                .create(WorldPayApiService::class.java)

And here is the annotated data object

@Root(name = "paymentService")
data class WorldPayXmlPaymentService(
        @field:Attribute(name = "version")
        var version: String = "",

        @field:Attribute(name = "merchantCode")
        var merchantCode: String = "",

        @field:Element(name = "reply", required = false)
        var reply: WorldPayXmlReply? = null,

        @field:Element(name = "inquiry", required = false)
        var inquiry: WorldPayXmlInquiry? = null
)
  • I do not see where in your code you are providing anything about the `DOCTYPE` (e.g., that DTD URL). There is a version of `create()` that takes a `Serializer` as input -- perhaps if you create your own SimpleXML `Serializer`, there is a way for you to feed in the `DOCTYPE` information. – CommonsWare Aug 01 '19 at 18:06
  • That's a great idea. I will try that. – Reed Abbott Aug 01 '19 at 19:13

1 Answers1

0

I added an override of a Persister as a parameter to the SimpleXmlConverterFactory.create method, like the following. It looks for the root element and then outputs the DOCTYPE before serializing the rest.

val persister = object : Persister() {
  @Throws(Exception::class)
  override fun write(source: Any, out: Writer) {
    (source as? WorldPayXmlPaymentService)?.let {
      val docType = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
        "<!DOCTYPE paymentService PUBLIC \"-//WorldPay//DTD WorldPay PaymentService v1//EN\" \"http://dtd.worldpay.com/paymentService_v1.dtd\">"
      out.write(docType)
      super.write(source, out)
    }
  }
}

val builder = Retrofit
                .Builder()
                .addConverterFactory(
                        SimpleXmlConverterFactory.create(persister)
                )
                .baseUrl(BuildConfig.WORLDPAY_BASE_URL)
                .build()
                .create(WorldPayApiService::class.java)

where WorldPayXmlPaymentService is my root data object.

Thanks to @CommonsWare for the guidance.