views:

675

answers:

4

I have to invoke a web service with a single parameter, a String in XML format. I'm building this via an XSLT transformation. So far so good.

The problem is with this XSD fragment:

<xs:complexType name="Document">
    <xs:sequence>
        <xs:element name="title" type="xs:string" minOccurs="1"/>
        <xs:element name="content" type="xs:base64Binary" minOccurs="1"/>
    </xs:sequence>
</xs:complexType>

which translates (for example) into this XML:

<attachment>
    <title>test title</title>
    <content>          
    PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiIHN0YW5kYWxvbmU9Inllc
    yI/Pg0KPG1zZ3ByYXRpY2E+DQogICAgPHByYXRpY2E+DQogICAgICAgIDxwcm9jZXNzbz
    4NCiAgICAgICAgICAgIDxjb2RQcm9jZXNzbz4xPC9jb2RQcm9jZXNzbz4NCiAgICAgICA
    gICAgIDxjb2RJc3RhbnphUHJvY2Vzc28MzwvY29kSXN0YW56YVByb2Nlc3NvPg0KICAgI
    CAgICAgICAgPGNvZFN0YXRvPjYwPC9jb2RTdGF0bz4NCiAgICAgICAgPC9wcm9jZXNzbz
    4NCiAgICA8L3ByYXRpY2E+DQo8L21zZ3ByYXRpY2E+
    </content>
</attachment>

Yes, you got it right: I have to insert a file content into the XML document in base 64 binary format.

I thought about inserting a placeholder with XSLT and then processing the XML document to replace it with the actual file content, but I'm wondering if there are any best practices for these occasions, maybe some fancy XSTL trick well beyond my knowledge or some Java tools which may come in handy.

How would you do that?

NOTE: I can't use SOAP with attachment, and I'm well aware that the aforementioned approach is prone to failure in case of huge attachments, but at the moment our counterpart will not budge.

+1  A: 

Probably the best way to do this is to read the file and encode its contents in base64 by the code that instantiates the XSLT transformation. The base64 string can either be passed as a parameter to the transformation, or the transformation could request it via an extension method.

In principle XSLT could be used to do the encoding to base64, however some byte values, such as 0 (for XML 1.0 and XML 1.1), and 29 other codes below 0x20 (for XML 1.0) are forbidden as characters within an XML document and this makes such encoding in XSLT impossible.

( see: http://projects.ischool.washington.edu/tabrooks/545/ContentManagement/PassingParameters.htm )

Dimitre Novatchev
A: 

This is not exactly the answer you are looking for, but XML processors like Woodstox link text support efficient reading and writing of inline Base64-encoded content. With Woodstox 4.0 it's done using so-called Typed Access API, part of Stax2 extension API (under package "org.codehaus.stax2.typed.*", TypedXMLStreamReader, TypedXMLStreamWriter).

This allows for streaming reading/writing of element and attribute values, given binary data.

The trick would then be to hook this up with XSLT. Another possibly simpler way is to figure out if client APIs Soap stacks (like CXF aka XFire) can give you access to content via XMLStreamReader/Writer. Most new ones are based on Stax, and usually embed Woodstox.

StaxMan
+1  A: 

If your SOAP stack supports MTOM, turn that on. What that will then do is put a "reference" in the content element to a MIME attachment where the data would be placed. That data can be completely binary and not base64 encoded.

Daniel Kulp
A: 

Here is how I would send the data on a base64Binary object (with MTOM or SwAref) in Java at least for testing purposes (as noted with the hard coded data path).

obj.setContent(
  new DataHandler(
    new URLDataSource(
      this.getClass().getResource("/someDocument.png")
    ));

Though the normal operation would be to use a FileDataSource to pass the data. Though I am pretty sure some one would have an idea of just creating your own data source. Generally I think that would be a bad idea because the most likely implementation is to use ByteArrayInputStream which uses VM memory and would blow up badly if you are sending a large file.

If you need to pass on a simple string for test purposes you can do the following:

obj.setContent(
  new DataHandler("test string", "text/plain");

Also if you are using WebSphere 7 runtimes, you may need to use the @MTOM annotation in your service implementation if you notice that the data being sent only contains the message, but no attachments.

Archimedes Trajano