Spring Object/XML Marshalling support providing generic interfaces for converting Java objects to XML and vice versa
MIME attachment support enables optimized handling of binary data in XML through MIME attachments using MTOM (Message Transmission Optimization Mechanism), XOP (XML-binary Optimized Packaging), or SwA (SOAP with Attachments) protocols.
Extended marshaller interface that supports MIME attachments during marshalling.
public interface MimeMarshaller extends Marshaller {
/**
* Marshals the object graph with the given root into the provided Result,
* writing binary data to a MimeContainer.
* @param graph the root of the object graph to marshal
* @param result the result to marshal to
* @param mimeContainer the MIME container to write extracted binary content to
* @throws XmlMappingException if the given object cannot be marshalled to the result
* @throws IOException if an I/O exception occurs
*/
void marshal(Object graph, Result result, @Nullable MimeContainer mimeContainer)
throws XmlMappingException, IOException;
}Extended unmarshaller interface that supports MIME attachments during unmarshalling.
public interface MimeUnmarshaller extends Unmarshaller {
/**
* Unmarshals the given provided Source into an object graph,
* reading binary attachments from a MimeContainer.
* @param source the source to marshal from
* @param mimeContainer the MIME container to read extracted binary content from
* @return the object graph
* @throws XmlMappingException if the given source cannot be mapped to an object
* @throws IOException if an I/O Exception occurs
*/
Object unmarshal(Source source, @Nullable MimeContainer mimeContainer)
throws XmlMappingException, IOException;
}Container interface for managing MIME attachments and XOP packages.
public interface MimeContainer {
/**
* Indicate whether this container is a XOP package.
* @return true when the constraints specified in XOP Documents are met
*/
boolean isXopPackage();
/**
* Turn this message into a XOP package.
* @return true when the message actually is a XOP package
*/
boolean convertToXopPackage();
/**
* Add the given data handler as an attachment to this container.
* @param contentId the content id of the attachment
* @param dataHandler the data handler containing the data of the attachment
*/
void addAttachment(String contentId, DataHandler dataHandler);
/**
* Return the attachment with the given content id, or null if not found.
* @param contentId the content id
* @return the attachment, as a data handler
*/
@Nullable
DataHandler getAttachment(String contentId);
}import org.springframework.oxm.mime.MimeMarshaller;
import org.springframework.oxm.mime.MimeContainer;
import jakarta.activation.DataHandler;
import jakarta.activation.ByteArrayDataSource;
import javax.xml.transform.stream.StreamResult;
import java.io.StringWriter;
// Assuming you have a MimeMarshaller implementation (like Jaxb2Marshaller)
MimeMarshaller mimeMarshaller = // ... get mime marshaller
// Create a MimeContainer implementation
MimeContainer mimeContainer = new MimeContainer() {
private Map<String, DataHandler> attachments = new HashMap<>();
private boolean isXop = false;
@Override
public boolean isXopPackage() { return isXop; }
@Override
public boolean convertToXopPackage() {
isXop = true;
return true;
}
@Override
public void addAttachment(String contentId, DataHandler dataHandler) {
attachments.put(contentId, dataHandler);
}
@Override
public DataHandler getAttachment(String contentId) {
return attachments.get(contentId);
}
};
// Object with binary data
DocumentWithAttachment document = new DocumentWithAttachment();
document.setTitle("My Document");
document.setContent("Document content");
document.setBinaryData(new byte[]{ /* large binary data */ });
// Marshal with MIME container
StringWriter writer = new StringWriter();
mimeMarshaller.marshal(document, new StreamResult(writer), mimeContainer);
// The XML will contain references to attachments
String xml = writer.toString();
// Binary data is stored in mimeContainer attachmentsimport org.springframework.oxm.mime.MimeUnmarshaller;
import javax.xml.transform.stream.StreamSource;
import java.io.StringReader;
// Assuming you have the XML and populated MimeContainer from marshalling
String xmlWithReferences = // ... XML with attachment references
MimeContainer populatedContainer = // ... container with attachments
MimeUnmarshaller mimeUnmarshaller = // ... get mime unmarshaller
// Unmarshal with MIME container
StringReader reader = new StringReader(xmlWithReferences);
DocumentWithAttachment document = (DocumentWithAttachment)
mimeUnmarshaller.unmarshal(new StreamSource(reader), populatedContainer);
// Binary data is restored from attachments
byte[] binaryData = document.getBinaryData();The Jaxb2Marshaller implements both MimeMarshaller and MimeUnmarshaller:
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import jakarta.xml.bind.annotation.XmlRootElement;
import jakarta.xml.bind.annotation.XmlMimeType;
import jakarta.activation.DataHandler;
// JAXB class with MIME type annotation
@XmlRootElement
public class DocumentWithImage {
private String title;
@XmlMimeType("image/jpeg")
private DataHandler imageData;
// getters and setters...
}
// Configure JAXB marshaller for MIME support
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(DocumentWithImage.class);
marshaller.setMtomEnabled(true); // Enable MTOM support
marshaller.afterPropertiesSet();
// Create document with image
DocumentWithImage doc = new DocumentWithImage();
doc.setTitle("Document with Image");
// Create DataHandler for image
byte[] imageBytes = // ... load image data
DataHandler imageHandler = new DataHandler(
new ByteArrayDataSource(imageBytes, "image/jpeg")
);
doc.setImageData(imageHandler);
// Marshal with MIME container
MimeContainer container = // ... your container implementation
StringWriter writer = new StringWriter();
marshaller.marshal(doc, new StreamResult(writer), container);// Working with XOP packages
MimeContainer container = // ... your container
// Check if container supports XOP
if (!container.isXopPackage()) {
// Convert to XOP package for optimized binary handling
boolean converted = container.convertToXopPackage();
if (converted) {
System.out.println("Container converted to XOP package");
}
}
// Add binary data as attachment
byte[] binaryData = // ... your binary data
DataHandler dataHandler = new DataHandler(
new ByteArrayDataSource(binaryData, "application/octet-stream")
);
container.addAttachment("binary-content-1", dataHandler);import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class SimpleMimeContainer implements MimeContainer {
private final Map<String, DataHandler> attachments = new ConcurrentHashMap<>();
private volatile boolean isXopPackage = false;
@Override
public boolean isXopPackage() {
return isXopPackage;
}
@Override
public boolean convertToXopPackage() {
this.isXopPackage = true;
return true;
}
@Override
public void addAttachment(String contentId, DataHandler dataHandler) {
if (contentId == null || dataHandler == null) {
throw new IllegalArgumentException("ContentId and DataHandler cannot be null");
}
attachments.put(contentId, dataHandler);
}
@Override
public DataHandler getAttachment(String contentId) {
return attachments.get(contentId);
}
// Additional utility methods
public Set<String> getAttachmentIds() {
return attachments.keySet();
}
public int getAttachmentCount() {
return attachments.size();
}
public void clearAttachments() {
attachments.clear();
}
}When using JAXB with MIME support, use these annotations:
import jakarta.xml.bind.annotation.*;
import jakarta.activation.DataHandler;
import java.awt.Image;
@XmlRootElement
public class MultimediaDocument {
@XmlElement
private String title;
// Binary data as DataHandler with MIME type
@XmlMimeType("image/jpeg")
private DataHandler photo;
// Binary data as byte array
@XmlMimeType("application/pdf")
private byte[] pdfContent;
// Image data
@XmlMimeType("image/png")
private Image diagram;
// getters and setters...
}Message Transmission Optimization Mechanism (MTOM) configuration:
import org.springframework.oxm.jaxb.Jaxb2Marshaller;
Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
marshaller.setClassesToBeBound(MultimediaDocument.class);
// Enable MTOM for optimized binary transfer
marshaller.setMtomEnabled(true);
// Configure attachment marshaller/unmarshaller if needed
marshaller.setAttachmentMarshaller(customAttachmentMarshaller);
marshaller.setAttachmentUnmarshaller(customAttachmentUnmarshaller);
marshaller.afterPropertiesSet();try {
MimeContainer container = new SimpleMimeContainer();
DocumentWithAttachment doc = new DocumentWithAttachment();
StringWriter writer = new StringWriter();
mimeMarshaller.marshal(doc, new StreamResult(writer), container);
} catch (XmlMappingException e) {
System.err.println("MIME marshalling failed: " + e.getMessage());
// Check for specific MIME-related errors
Throwable cause = e.getCause();
if (cause instanceof jakarta.xml.bind.MarshalException) {
System.err.println("JAXB marshalling error: " + cause.getMessage());
}
}import org.springframework.oxm.mime.MimeMarshaller;
import org.springframework.oxm.mime.MimeUnmarshaller;
import org.springframework.oxm.mime.MimeContainer;
import org.springframework.oxm.XmlMappingException;
import jakarta.activation.DataHandler;
import jakarta.activation.DataSource;
import jakarta.activation.ByteArrayDataSource;
import jakarta.xml.bind.annotation.XmlMimeType;
import jakarta.xml.bind.attachment.AttachmentMarshaller;
import jakarta.xml.bind.attachment.AttachmentUnmarshaller;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.concurrent.ConcurrentHashMap;Install with Tessl CLI
npx tessl i tessl/maven-org-springframework--spring-oxm