I am trying to use the XSLT component to do dynamic transformation from XML. Is it possible to pass in a java variable in the URI as XSLT template?
For example:
from("direct:foo").
to("xslt:${fee}").
to("direct:output");
foo - is a XML payload,
fee - XSLT template stored as java.lang.String,
output - xml payload
You can assign your variable into message header by some conditions:
.setHeader("TemplateLocation").constant("OSGI-INF/xsl/pretty.xsl")
After, you can use Recipient List EIP:
.recipientList().simple("xslt:${header.TemplateLocation}")
or you can use toD:
.toD("xslt:${header.TemplateLocation}")
Working example:
#Override
protected RouteBuilder createRouteBuilder() {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.routeId("xsltTest")
.log(LoggingLevel.INFO, "XML input : \n${body}")
.setHeader("TemplateLocation").constant("OSGI-INF/xsl/pretty.xsl")
//.recipientList().simple("xslt:${header.TemplateLocation}")
.toD("xslt:${header.TemplateLocation}")
.to("log:end?level=INFO&showAll=true&multiline=true");
}
};
}
And there is no way to use a string variable as an xslt template, as far as I know.
Based on my knowledge
Your XSLT poller has a dynamic expression as subdirectory (${fee}).
As far as I know you cannot have a dynamic from address in a Camel
route. Because this would mean that you could consume from multiple
endpoints.
You can have it as separate file and call it like this
to("xslt:file:///foo/bar.xsl").
For more details XSLT
You cannot use a dynamic stylesheet (dynamic content) with the XSL component of Camel.
The most dynamic thing you can do is a dynamic reference to a static file like this:
.toD("xslt:${expressionWithFileReference}")
However, you can simply call a Java bean to do what you want and call it from the route:
.bean(javaBeanReference or new YourJavaBean())
In the Bean you can use Camel annotations to inject header(s), properties and the body into a method. Whatever you need from the current Camel Exchange.
public void yourMethod(
#Header(headername) String parameterName,
#Body Type parameterName) {
...
}
As Camel does not have support for dynamic XSLT input stream, I had to create my own Transformer. This might help someone
Here is my code snippet. I used camel processor as below
#Override
public void process(Exchange exchange) throws Exception {
XmlMapper xmlMapper = new XmlMapper();
Target target = xmlMapper.readValue(transform(getInputStreamFromDocument(xmlPayload), new ByteArrayInputStream(xsltTemplate.getBytes())), target.class);
}
public byte[] transform(InputStream dataXML, InputStream inputXSL)
throws TransformerException {
ByteArrayOutputStream bos = new ByteArrayOutputStream();
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer(new StreamSource(inputXSL));
StreamSource in = new StreamSource(dataXML);
StreamResult out = new StreamResult(bos);
transformer.transform(in, out);
return bos.toByteArray();
}
Related
i'm having problems transfering a custom object to the client. How can i transfer a custom object to the client and receive it back to the webservice? i'm transferring a file by chunks. i want to know how i should write my client. i tried passing it as MediaType.APPLICATION_JSON in client but i get no result meaning it doesn't get passed back to the webservice. Below is a bit of code im working on.
Webservice
#POST
#Path("/fileTransfer")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public final TransferInfomation transferInfo(final FileModel file)
{
...
}
...(some code)(lets just say a syso)
FileModel Class
public class FileModel {
private String fileID;
private DataHandler dataHandler;
/**
* Constructor.
*/
public FileModel() {
}
(lets assume setters and getters are made)
(Not sure if the webservice is correct). Still learning REST, i want to know how the client should be.
thanks in advance.
A good way to "marshal" and "unmarshal" "custom objects" (in JSON, XML, etc.) in Jersey is to use JAXB (https://jaxb.java.net/).
To do this you need to create a "jaxb class", with the proper getters and setters (and annotations), e.g.:
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement
public class FileModel{
private String fileID;
private DataHandler dataHandler;
public String getFileID(){
return fileID;
}
public void setFileID(String fileID){
this.fileID = fileID;
}
public DataHandler getDataHandler(){
return dataHandler;
}
public void setDataHandler(DataHandler dataHandler){
this.dataHandler = dataHandler;
}
}
Do not forget to declare the #XmlRootElement. Then you can simply declare and use these objects in your API endpoints (methods):
#POST
#Path("/fileTransfer")
#Consumes({MediaType.APPLICATION_JSON})
#Produces({MediaType.APPLICATION_JSON})
public final FileModel transferInfo(FileModel file)
{
// read file in "FileModel" format
// ... make several operations
// return new FileModel (or another format if you will)
}
This should work. Make sure you follow the data structure defined for FileModel correctly in the client side. See here a example on how to handle that in Jersey: How do I POST a Pojo with Jersey Client without manually convert to JSON? (where JAXB is also used).
Your REST endpoint indicates you want to consume and produce JSON. So the REST client needs to send JSON that can be deserialized into FileModel, and the TransferInfomation returned by transferInfo needs to be serialized into JSON to return to the client.
Typically, Java REST frameworks like RESTEasy, Restlet, Camel, and Spring MVC provide facilities that let you define a JSON serializer/deserializer like Jackson and the mapping rules from JSON <--> Java, and the framework handles the details for you.
So if you use one of these frameworks, you will just have to configure them to use the preferred JSON tool and define the rules.
You can achive this like below:
Server Side:
#PUT
#Consumes(MediaType.APPLICATION_XML)
#Produces(MediaType.APPLICATION_XML)
public String addRecord(CustomClass mCustomClass)
{
///
///
///
return "Added successfully : "+CustomClass.getName();
}// addRecord
Client Side:
public static void main(String[] args)
{
///
///
///
CustomClass mCustomClass = new CustomClass();
Client client = ClientBuilder.newClient();
String strResult = client.target(REST_SERVICE_URL).request(MediaType.APPLICATION_XML).put(Entity.xml(mCustomClass), String.class);
}
This is the code I have for Saxon Transformation of XSLT files which accepts xml and xslt and returns a transformed string. I can have either xsl 1.0 or 2.0 get processed through this function.
DocumentBuilder requires a BaseURI, even if I don't have any file format. I have provided "c:\\" as the BaseURI, inspite I have nothing to do with this directory.
Is there any better way to achieve this thing or write this function?
public static string SaxonTransform(string xmlContent, string xsltContent)
{
// Create a Processor instance.
Processor processor = new Processor();
// Load the source document into a DocumentBuilder
DocumentBuilder builder = processor.NewDocumentBuilder();
Uri sUri = new Uri("c:\\");
// Now set the baseUri for the builder we created.
builder.BaseUri = sUri;
// Instantiating the Build method of the DocumentBuilder class will then
// provide the proper XdmNode type for processing.
XdmNode input = builder.Build(new StringReader(xmlContent));
// Create a transformer for the stylesheet.
XsltTransformer transformer = processor.NewXsltCompiler().Compile(new StringReader(xsltContent)).Load();
// Set the root node of the source document to be the initial context node.
transformer.InitialContextNode = input;
StringWriter results = new StringWriter();
// Create a serializer.
Serializer serializer = new Serializer();
serializer.SetOutputWriter(results);
transformer.Run(serializer);
return results.ToString();
}
If you think that the base URI will never be used (because you never do anything that depends on the base URI) then the best strategy is to set a base URI that will be instantly recognizable if your assumption turns out to be wrong, for example "file:///dummy/base/uri".
Choose something that is a legal URI (C:\ is not).
I am trying to receive data from the Web Service and I am getting the Data from Web Service back but it is form of [object Object]. Can anybody help me on this.
Below is the code for my web service:
public class WebServiceAccess
{
private var webService:WebService;
private var serviceOperation:AbstractOperation;
private var myValueObjects:ValueObjects;
private var method:String;
[Bindable]
public var employeeData:ArrayCollection;
[Bindable]
public var employees:ArrayCollection;
public function WebServiceAccess(url:String, method:String)
{
webService = new WebService();
this.method = method;
webService.loadWSDL(url);
webService.addEventListener(LoadEvent.LOAD, ServiceRequest);
}
public function ServiceRequest():void
{
serviceOperation = webService.getOperation(method);
serviceOperation.addEventListener(FaultEvent.FAULT, DisplayError);
serviceOperation.addEventListener(ResultEvent.RESULT, DisplayResult);
serviceOperation.send();
}
public function DisplayError(evt:FaultEvent):void
{
Alert.show(evt.fault.toString());
}
public function DisplayResult(evt:ResultEvent):void
{
employeeData = evt.result as ArrayCollection;
Alert.show(employeeData.toString());
}
}
First of all, evt.result is not an ArrayCollection, it is an Object (unless your SOAP service/WSDL are completely screwed up/malformed XML).
Second, you can't just display an Array or ArrayCollection (or generic Object, even) as a String (even though the .toString() method always seems to imply that) anyway, you have to parse the data to get what you want from it.
Now, the WebService class is nice in that it automatically parses the XML file that a SOAP service returns into a single usable Object. So that is actually the hard part.
What you need to do is call various properties of the object to get the data you need.
So if the XML return (look at your WSDL to see what the return should be, I also highly suggest soapUI) is this:
<employee name="Josh">
<start date="89384938984"/>
<photo url="photo.jpg"/>
</employee>
And you wanted to display "Josh" and the photo, you would do this.
var name:String = e.result.employee.name;
var url:String = e.result.employee.photo.url;
It does get more complicated. If the WSDL allows for multiple nodes with the same name at the same level, it does return an ArrayCollection. Then you have to loop through the array and find the exact item you need.
Just remember: The WSDL is god. Period. If it says there can be multiple "employee" nodes, you have to code accordingly, even if you don't see more than one in your tests. The issue is that there always could be multiple nodes.
I'm trying to implement simple web service client for PayPal Express Checkout API using JAX WS. PayPal Express Checkout API provides WSDL file, from which I was able to generate Java classes using CXF's wsdl2java utility.
From authentication reasons, it demands adding SOAP Header to each request. This header is quite simple and should look like here:
https://cms.paypal.com/us/cgi-bin/?cmd=_render-content&content_ID=developer/e_howto_api_ECSOAPAPIBasics#id09C3I0CF0O6
Generated from WSDL classes include ebay.apis.eblbasecomponents.CustomSecurityHeaderType class which represents header which I need to add to each request.
So the question is: how can I add manually created instance of CustomSecurityHeaderType class to SOAP request's header taking into account following conditions:
I'm not very eager to use classes from com.sun.* package as mentioned in answer here: JAX-WS - Adding SOAP Headers (mainly because of possible portability issues between different JDK's)
I don't want to manually marshal that object into nested javax.xml.soap.SOAPElement instances as mentioned in answer here:
How do I add a SOAP Header using Java JAX-WS
So, it looks like I've found possible answer while combining JAX-WS & JAXB related answers from SO (I would really appreciate if somebody experienced in these technologies can check whether following is correct):
The obvious thing for me is to add SOAP message handler and alter header of SOAPMessage instance in it:
import javax.xml.ws.Binding;
import javax.xml.ws.BindingProvider;
import javax.xml.ws.handler.Handler;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.soap.SOAPHeader;
import ebay.api.paypalapi.ObjectFactory; // class generated by wsdl2java
// following class is generated by wsdl2java utility Service class
final PayPalAPIInterfaceService payPalService = new PayPalAPIInterfaceService();
final PayPalAPIAAInterface expressCheckoutPort = payPalService.getPayPalAPIAA();
final Binding binding = ((BindingProvider) expressCheckoutPort).getBinding();
List<Handler> handlersList = new ArrayList<Handler>();
// now, adding instance of Handler to handlersList which should do our job:
// creating header instance
final CustomSecurityHeaderType headerObj = new CustomSecurityHeaderType();
final UserIdPasswordType credentials = new UserIdPasswordType();
credentials.setUsername("username");
credentials.setPassword("password");
credentials.setSignature("signature");
headerObj.setCredentials(credentials);
// bookmark #1 - please read explanation after code
final ObjectFactory objectFactory = new ObjectFactory();
// creating JAXBElement from headerObj
final JAXBElement<CustomSecurityHeaderType> requesterCredentials = objectFactory.createRequesterCredentials(headerObj);
handlersList.add(new SOAPHandler<SOAPMessageContext>() {
#Override
public boolean handleMessage(final SOAPMessageContext context) {
try {
// checking whether handled message is outbound one as per Martin Strauss answer
final Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(CustomSecurityHeaderType.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(requesterCredentials, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
// ... default implementations of other methods go here
});
// as per Jean-Bernard Pellerin's comment setting handlerChain list here, after all handlers were added to list
binding.setHandlerChain(handlersList);
Explanation of bookmark #1:
one should marshal not the header object itself, but JAXBElement representing that object, because otherwise one will get an exception. One should use one of ObjectFactory classes which are generated from WSDL for creating needed JAXBElement instances from original objects.
(Thanks #skaffman for answer: No #XmlRootElement generated by JAXB )
One should also refer to Martin Straus answer which extends this one
This solution works great, but there's a catch. It generates this error when the inbound message is processed:
dic 19, 2012 7:00:55 PM com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl addHeader
SEVERE: SAAJ0120: no se puede agregar una cabecera si ya hay una
Exception in thread "main" javax.xml.ws.WebServiceException: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:167)
at com.sun.xml.ws.handler.HandlerTube.processResponse(HandlerTube.java:174)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1074)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:979)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:950)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:825)
at com.sun.xml.ws.client.Stub.process(Stub.java:443)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:174)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:102)
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:154)
at $Proxy38.wsRdyCrearTicketDA(Unknown Source)
at ar.com.fit.fides.remedy.api.ws.ServicioCreacionTickets.crearTicket(ServicioCreacionTickets.java:55)
at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.crearTicket(ConectorRemedyWS.java:43)
at ar.com.fit.fides.remedy.api.ws.ConectorRemedyWS.main(ConectorRemedyWS.java:90)
Caused by: java.lang.RuntimeException: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:50)
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:23)
at com.sun.xml.ws.handler.HandlerProcessor.callHandleMessageReverse(HandlerProcessor.java:341)
at com.sun.xml.ws.handler.HandlerProcessor.callHandlersResponse(HandlerProcessor.java:214)
at com.sun.xml.ws.handler.ClientSOAPHandlerTube.callHandlersOnResponse(ClientSOAPHandlerTube.java:161)
... 14 more
Caused by: com.sun.xml.messaging.saaj.SOAPExceptionImpl: Can't add a header when one is already present.
at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:128)
at com.sun.xml.messaging.saaj.soap.impl.EnvelopeImpl.addHeader(EnvelopeImpl.java:108)
at ar.com.fit.fides.remedy.api.ws.AuthenticationHandler.handleMessage(AuthenticationHandler.java:45)
So, the solution is to check whether the message being handled if the outbound message, like this:
public boolean handleMessage(SOAPMessageContext context) {
try {
Boolean outbound = (Boolean) context.get("javax.xml.ws.handler.message.outbound");
if (outbound != null && outbound) {
// obtaining marshaller which should marshal instance to xml
final Marshaller marshaller = JAXBContext.newInstance(AuthenticationInfo.class).createMarshaller();
// adding header because otherwise it's null
final SOAPHeader soapHeader = context.getMessage().getSOAPPart().getEnvelope().addHeader();
// marshalling instance (appending) to SOAP header's xml node
marshaller.marshal(info, soapHeader);
}
} catch (final Exception e) {
throw new RuntimeException(e);
}
return true;
}
I created a web service exposing method with params user and password as header like this:
#WebService(serviceName="authentication")
public class WSAuthentication {
String name = null;
String password = null;
public WSAuthentication() {
super();
}
public WSAuthentication(String name, String password) {
this.name = name;
this.password = password;
}
private static String getData(WSAuthentication sec) {
System.out.println("********************* AUTHENTICATION ********************" + "\n" +
"**********USER: " + sec.name + "\n" +
"******PASSWORD: " + sec.password + "\n" +
"******************************** AUTHENTICATION ****************************");
return sec.name + " -- " + sec.password;
}
#WebMethod(operationName="security", action="authenticate")
#WebResult(name="answer")
public String security(#WebParam(header=true, mode=Mode.IN, name="user") String user, #WebParam(header=true, mode=Mode.IN, name="password") String password) {
WSAuthentication secure = new WSAuthentication(user, password);
return getData(secure);
}
}
Try compiling it and testing generated from WSDL class. I hope this helps.
I found this answer:
JAX-WS - Adding SOAP Headers
Basically you add -XadditionalHeaders to the compiler options and objects in the headers also appear in your generated code as parameters of the method.
If you are using maven, and the jaxws-maven-plugin all you have to do is add the xadditionalHeaders flag to true and the client will be generated with the methods that have the headers as input.
https://jax-ws-commons.java.net/jaxws-maven-plugin/wsimport-mojo.html#xadditionalHeaders
I am working in a JAXWS/JAXB web service environment. JAXWS out of the box uses uses the JAXB to marshal/unmarshaler the web service payloads.
I also have a requirement to audit all request and response payloads.
I want a compact and concise marshaled representation of the payload for the audit (as a irrelevant side note - I am auditing using a java.util.concurrent.BlockingQueue and some consumer threads to put batches of audit data in the audit datasource).
I have binary content(mtom) included on some web service response payloads but I DO NOT want to marshal audit these because the serialized base64 would be too large.
So my need is to create a marshaller (exclusively for auditng) that in all cases will scrub binary content but then NOT scrub for the prime purpose of marshalling web service response payloads. I do XSD to Java xjc. I need to use the same XSD/JAXB namespace for both contexts/marshallers.
Java type converter:
<jxb:javaType name=""
parseMethod="com.xxx.xxx.ws.converter.XXXLongConverter.parseXXXLong"
printMethod="com.xxx.xxx.ws.converter.XXXLongConverter.printXXXLong" />
is will not work because 1. I would need to unregister the adapter http://docs.oracle.com/javase/6/docs/api/javax/xml/bind/Marshaller.html#setAdapter%28java.lang.Class,%20A%29
for the marshaller and I don't THINK I have a hook into that for JAXWS. 2. I can't be guaranteed the class name that JAXB will decide to create in order to unregister it.
I created my own XMLAdapter and used the annox jaxb plugin
but that didn't really work for the same reasons the above didn't work.
Update: I now tried manually and reflectively walking through payload(to be audited) prior to marshalling to scrub the binary data but that got to be too much pain for what it was worth.
I should also mention that for brevity of the audit I am using jersey JSON serialization supporting JAXB
but I don't think that takes away or adds to my base problem:
How can I scrub data in one marshaller/unmarshaller but not another but both whose origin is the same JAXB context?
UPDATE: Never figured out an elegate way to do this. Not really possible at this point with the frameworks as they are. UPDATE: Not true. Extending AttachmentMarshaller (I like this a lot and will use it) or creating a "need-aware" XmlAdapter would work for the audit specific marshaller as #Blaise answers below.
UPDATE: If I may take this a step further to round out my use case...I mentioned above that for brevity of the audit I'd like to do Json Serialization of the JSONJAXBContext using jersey apis, specifically using the JSONMarshaller but the interface does not define setAdapter and setAttachmentMarshaller. Coming out of JSONJAXBContext.createJSONMarshaller() is a JSONMarshallerImpl implementation which do define these this methods. I will grudgingly cast to impl so I can set my custom attachment marshaller.
How can I scrub data in one marshaller/unmarshaller but not another
but both whose origin is the same JAXB context?
You could set your own implementation of AttachemntMarshaller and set it on the Marshaller that you are using for auditing.
Root
Below is a sample domain object with a byte[] property that by default will be represented as an element of type base64Binary.
package forum8914008;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
byte[] bytes;
}
Demo
The demo code below first marshals the object to XML, and then marshals it a second time with a custom impplementation of AttachmentMarshaller set.
package forum8914008;
import javax.activation.DataHandler;
import javax.xml.bind.*;
import javax.xml.bind.attachment.AttachmentMarshaller;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.bytes = "Hello World".getBytes();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
marshaller.setAttachmentMarshaller(new AttachmentMarshaller() {
#Override
public boolean isXOPPackage() {
return true;
}
#Override
public String addMtomAttachment(DataHandler arg0, String arg1,
String arg2) {
return "fake";
}
#Override
public String addMtomAttachment(byte[] arg0, int arg1, int arg2,
String arg3, String arg4, String arg5) {
return "fake";
}
#Override
public String addSwaRefAttachment(DataHandler arg0) {
return "fake";
}
});
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code. The first XML document could grow to be quite large if the byte[] was big. The second XML document would stay the same size.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>
<xop:Include xmlns:xop="http://www.w3.org/2004/08/xop/include" href="fake"/>
</bytes>
</root>
How can I scrub data in one marshaller/unmarshaller but not another
but both whose origin is the same JAXB context?
You could support this use case with an XmlAdapter.
XmlAdapter (ByteArrayAdapter)
The following XmlAdapter is used to convert a byte[] to a byte[]. In its default state it will return the original byte[], it also has a audit state where it will return an empty byte[].
package forum8914008;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class ByteArrayAdapter extends XmlAdapter<byte[], byte[]> {
private boolean audit;
public ByteArrayAdapter() {
this(false);
}
public ByteArrayAdapter(boolean audit) {
this.audit = audit;
}
#Override
public byte[] marshal(byte[] bytes) throws Exception {
if(audit) {
return new byte[0];
}
return bytes;
}
#Override
public byte[] unmarshal(byte[] bytes) throws Exception {
return bytes;
}
}
package-info
The #XmlJavaTypeAdapter annotation is used tp register the XmlAdapter. When used at the package level it will apply to all properties of the specified type in that package (see: http://blog.bdoughan.com/2012/02/jaxb-and-package-level-xmladapters.html).
#XmlJavaTypeAdapter(value=ByteArrayAdapter.class, type=byte[].class)
package forum8914008;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
Root
Below is a sample domain object with a byte[] property that by default will be represented as an element of type base64Binary.
package forum8914008;
import javax.xml.bind.annotation.*;
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Root {
byte[] bytes;
}
Demo
The demo code below first marshals the object with the default state of the ByteArrayAdapter which will return the real byte[] and the marshals the object a second time with a stateful ByteArrayAdapter set which will convert all byte[] values to an empty byte[].
package forum8914008;
import javax.xml.bind.*;
public class Demo {
public static void main(String[] args) throws Exception {
JAXBContext jc = JAXBContext.newInstance(Root.class);
Root root = new Root();
root.bytes = "Hello World".getBytes();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(root, System.out);
marshaller.setAdapter(new ByteArrayAdapter(true));
marshaller.marshal(root, System.out);
}
}
Output
Below is the output from running the demo code. The XmlAdapter would apply to all mapped fields/properties of type byte[].
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes>SGVsbG8gV29ybGQ=</bytes>
</root>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<bytes></bytes>
</root>