Feign provides comprehensive XML support through JAXB and SAX for XML request and response handling.
JAXB-based XML encoding and decoding for object-to-XML marshalling.
/**
* XML encoder using JAXB for marshalling objects to XML
*/
public class JAXBEncoder implements Encoder {
/** Create encoder with default JAXB context factory */
public JAXBEncoder();
/** Create encoder with custom JAXB context factory */
public JAXBEncoder(JAXBContextFactory jaxbContextFactory);
/** Encode object to XML request body */
public void encode(Object object, Type bodyType, RequestTemplate template) throws EncodeException;
}
/**
* XML decoder using JAXB for unmarshalling XML to objects
*/
public class JAXBDecoder implements Decoder {
/** Create decoder with default JAXB context factory */
public JAXBDecoder();
/** Create decoder with custom JAXB context factory */
public JAXBDecoder(JAXBContextFactory jaxbContextFactory);
/** Decode XML response to object */
public Object decode(Response response, Type type) throws IOException, DecodeException;
}
/**
* Factory for creating and caching JAXB contexts
*/
public interface JAXBContextFactory {
/** Create or retrieve cached JAXB context for classes */
JAXBContext createContext(Class<?>... classes) throws JAXBException;
/** Default implementation with context caching */
public static class Default implements JAXBContextFactory;
}SAX-based XML parsing for streaming XML processing.
/**
* XML decoder using SAX parser for efficient XML processing
*/
public class SAXDecoder implements Decoder {
/** Create decoder builder for configuration */
public static Builder builder();
/** Decode XML response using registered content handlers */
public Object decode(Response response, Type type) throws IOException, DecodeException;
/**
* Builder for configuring SAX decoder
*/
public static class Builder {
/** Register content handler for specific type */
public Builder registerContentHandler(Class<?> type, ContentHandlerFactory contentHandlerFactory);
/** Register content handler class */
public Builder registerContentHandler(Class<? extends DefaultHandler> handlerClass);
/** Build configured SAX decoder */
public SAXDecoder build();
}
}import feign.Feign;
import feign.jaxb.JAXBEncoder;
import feign.jaxb.JAXBDecoder;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlElement;
@XmlRootElement(name = "user")
class User {
@XmlElement(name = "id")
private String id;
@XmlElement(name = "name")
private String name;
@XmlElement(name = "email")
private String email;
// constructors, getters, setters
}
// Basic JAXB configuration
UserAPI userAPI = Feign.builder()
.encoder(new JAXBEncoder())
.decoder(new JAXBDecoder())
.target(UserAPI.class, "https://api.example.com");
// API calls automatically marshal/unmarshal XML
User user = userAPI.getUser("123");
userAPI.createUser(new User("456", "John Doe", "john@example.com"));import feign.jaxb.JAXBContextFactory;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
class CustomJAXBContextFactory implements JAXBContextFactory {
@Override
public JAXBContext createContext(Class<?>... classes) throws JAXBException {
// Custom context creation with specific configuration
return JAXBContext.newInstance(classes);
}
}
UserAPI userAPI = Feign.builder()
.encoder(new JAXBEncoder(new CustomJAXBContextFactory()))
.decoder(new JAXBDecoder(new CustomJAXBContextFactory()))
.target(UserAPI.class, "https://api.example.com");import feign.Feign;
import feign.sax.SAXDecoder;
import org.xml.sax.helpers.DefaultHandler;
import org.xml.sax.Attributes;
// Custom SAX content handler
class UserHandler extends DefaultHandler {
private User user;
private StringBuilder currentValue = new StringBuilder();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) {
if ("user".equals(qName)) {
user = new User();
}
currentValue.setLength(0);
}
@Override
public void characters(char[] ch, int start, int length) {
currentValue.append(ch, start, length);
}
@Override
public void endElement(String uri, String localName, String qName) {
if ("id".equals(qName)) {
user.setId(currentValue.toString());
} else if ("name".equals(qName)) {
user.setName(currentValue.toString());
} else if ("email".equals(qName)) {
user.setEmail(currentValue.toString());
}
}
public User getUser() {
return user;
}
}
// Configure SAX decoder
UserAPI userAPI = Feign.builder()
.decoder(SAXDecoder.builder()
.registerContentHandler(UserHandler.class)
.build())
.target(UserAPI.class, "https://api.example.com");import javax.xml.bind.annotation.*;
import java.util.List;
@XmlRootElement(name = "users")
@XmlAccessorType(XmlAccessType.FIELD)
class UserList {
@XmlElement(name = "user")
private List<User> users;
// constructors, getters, setters
}
@XmlRootElement(name = "user")
@XmlAccessorType(XmlAccessType.FIELD)
class User {
@XmlAttribute
private String id;
@XmlElement
private String name;
@XmlElement
private String email;
@XmlElement(name = "address")
private Address address;
@XmlElementWrapper(name = "tags")
@XmlElement(name = "tag")
private List<String> tags;
}
@XmlAccessorType(XmlAccessType.FIELD)
class Address {
private String street;
private String city;
private String country;
}
interface UserAPI {
@RequestLine("GET /users")
@Headers("Accept: application/xml")
UserList getAllUsers();
@RequestLine("GET /users/{id}")
@Headers("Accept: application/xml")
User getUser(@Param("id") String id);
@RequestLine("POST /users")
@Headers({"Content-Type: application/xml", "Accept: application/xml"})
User createUser(User user);
}@XmlRootElement(name = "user", namespace = "http://example.com/user")
@XmlAccessorType(XmlAccessType.FIELD)
class User {
@XmlElement(namespace = "http://example.com/user")
private String id;
@XmlElement(namespace = "http://example.com/user")
private String name;
}
// JAXB automatically handles namespaces
UserAPI userAPI = Feign.builder()
.encoder(new JAXBEncoder())
.decoder(new JAXBDecoder())
.target(UserAPI.class, "https://api.example.com");dependencies {
compile 'com.netflix.feign:feign-jaxb:8.18.0'
}dependencies {
compile 'com.netflix.feign:feign-sax:8.18.0'
}// Custom marshaller properties
JAXBContextFactory factory = new JAXBContextFactory() {
@Override
public JAXBContext createContext(Class<?>... classes) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(classes);
return context;
}
};
// Configure marshaller/unmarshaller
Marshaller marshaller = context.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");// Custom error decoder for XML APIs
ErrorDecoder xmlErrorDecoder = new ErrorDecoder() {
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400) {
try {
// Parse XML error response
ErrorResponse error = (ErrorResponse) new JAXBDecoder()
.decode(response, ErrorResponse.class);
return new CustomAPIException(error.getMessage(), error.getCode());
} catch (Exception e) {
return new FeignException(response.status(), "XML parsing failed");
}
}
return null;
}
};
UserAPI userAPI = Feign.builder()
.encoder(new JAXBEncoder())
.decoder(new JAXBDecoder())
.errorDecoder(xmlErrorDecoder)
.target(UserAPI.class, "https://api.example.com");