Say I have a data class in a JAX-RS 1 environment (RestEasy 2 with the Jackson provider) like this:
class Foo {
int id;
String name;
Bar bar;
...
}
with Bar being:
class Bar {
int one;
String two;
}
Now I want to have Bar serialized in a special way (perhaps depending on the media type that was requested (or depending the phase of the moon), I would write a MessageBodyWriter<Bar>
#Provider
#Produces("application/json")
public class BarWriter implements MessageBodyWriter<Bar> {
...
}
which works very well if Bar is requested on its own like in
#GET #Path("bar")
public Bar getBar() { return new Bar(...); }
But when I request Foo as in
#GET #Path("foo")
public Foo getFoo() { return new Foo(...); }
the message body writer is ignored.
Now what I want is that this MessageBodyWriter is also used when I return Foo or a List<Bar>
I think the latter can be achieved by just writing a custom MessageBodyWriter for the List case, but for the former case I can't write a message body writer for all my application classes that contain a Bar field.
Any ideas on how to solve this? I was also trying to use a Jackson serializer on the Bar instance, but it looks like this is not even registered by RestEasy (and then, I think that way is too fragile anyway).
Unfortunately, this is not how message body writers work. The JAX-RS implementation will locate a writer, to be used in serialization, based on the type being returned from your resource method. So in your case, with a custom writer defined for Bar, with this resource method:
#GET #Path("bar")
public Bar getBar() { return new Bar(...); }
the JAX-RS provider will serialize Bar using your custom writer. However for this resource method:
#GET #Path("foo")
public Foo getFoo() { return new Foo(...); }
you do not have a custom writer defined, and serialization will be handled by the first matching (default) provider that can handle the combination of return class and content-type. A key thing to remember is that, unlike typical JSON and XML serialization libraries, JAX-RS entity providers are not recursive. Aka, for a given object A being returned in a resource method, the provider will attempt to locate a custom writer only for A, and not for any of the types included in A as variables.
Since you are using Jackson though, why not just define a custom serializer for your Bar class? That will handle pretty much every scenario you described:
public class BarSerializer extends JsonSerializer<Bar> {
#Override
public void serialize(final Bar value, final JsonGenerator jgen,
final SerializerProvider provider) throws IOException,
JsonProcessingException {
jgen.writeStartObject();
jgen.writeFieldName("myBar");
jgen.writeString(value.getTwo());
jgen.writeEndObject();
}
}
You tell Jackson to use this custom serializer thusly:
#JsonSerialize(using=BarSerializer.class)
class Bar {
int one;
String two;
}
Lastly, don't forget that if you anticipate getting JSON back in the same form as you serialized, that you will also need a custom JsonDeserializer.
To get it to work, you need the jackson-mapper and jackson-jaxrs jars in your classpath (and probably the jackson-core one as well).
The JAX-RS runtime will only look up one MessageBodyWriter for the object returned by the resource method (see sectioin "4.2.2 Message Body Writer" in the specification), then that single MessageBodyWriter has complete control over the serialization of the entire object graph to be returned to the client.
In order to implement the behavior you wanted, you would need a custom MessageBodyWriter per media type, that is willing to delegate the serialization of a part of the object graph to another writer whenever it encounters a specific type in the graph, and then resume its own logic. Obtaining the delegate writer for the specific type wouldn't be a big problem (inject a javax.ws.rs.ext.Providers and call getMessageBodyWriter()), but I don't think the existing xml/json/etc serializers are implemented with such kind of extensions in mind, so I guess you couldn't just relay on them. Reimplementing an xml marshaller just for this is not an attractive option either.
Refer the below post for writing custom message body writer for your Java object's serialization.
http://h2labz.blogspot.in/2014/12/marshalling-java-to-json-in-jax-rs.html
Related
I have a model class which has around 45 properties. I have created another DTO class which has exactly the same properties.
At runtime, some requirements dont need me to show all the properties to the user. Hence i want to copy some properties from my model class to my DTO class and then send that object to the client.
I am using Spring.s BeanUtils.copyproperties. But here i only see the option to ignore properties which i dont want. as the my list of unwanted properties is long, is there a way in which i can specify only the list i want.
I searched on the net and found a solution
"org.springframework.beans.BeanUtils.copyProperties(Object source, Object target, Class editable) throws BeansException
Ensure the target implements the interface editable which defines the properties which would be copied."
But i am not able to work my head around this editable interface. I tried using an interface which has all the properties I want and tried to use it here, but it gave me an error saying that it is expecting a class. Can some body help me with the editable interface stuff
I think creating another DTO with just the properties you wanted will be a lot easier.
Instead of creating copies of the object containing only a few properties, you could define various Interfaces that provide only the getter methods to the properties that you want to expose to the consumer of your object. Your DTO can implement all the different interfaces. And you hand over the interface to the consumer instead of a concrete class.
public class MyDTO implements Fooable, Barable {
private String foo;
private String bar;
public String getFoo() {
return foo;
}
public String getBar() {
return bar;
}
}
public interface Fooable {
String getFoo();
}
public interface Barable {
String getBar();
}
I'm getting familiar with web services in Java using Jax-ws (or JAXB, not sure, anyway...).
I've created small project with a single webservice. The WS has the only endpoint called transfer and returns objects inheriting ITransferResult interface.
Web service contract
//Service Endpoint Interface
#WebService
#SOAPBinding(style = Style.RPC)
public interface IBankWebSrv {
#WebMethod
ITransferResult transfer(String accountNumber, double amount);
}
Web service implementation
//Service Implementation
#WebService(endpointInterface = "Contracts.IBankWebSrv")
public class BankWebSrv implements IBankWebSrv {
#Override
public ITransferResult transfer(String accountNumber, double amount) {
ITransferResult result = new TransferResult();
// TODO logic here
result.setSuccessful(true);
return result;
}
}
TransferResult contract
#XmlJavaTypeAdapter(TransferResult.class)
public interface ITransferResult {
boolean isSuccessful();
void setSuccessful(boolean successful);
}
TransferResult implementation
public class TransferResult extends XmlAdapter<TransferResult, ITransferResult>
implements ITransferResult {
#XmlElement
boolean successful;
public boolean isSuccessful() {
return this.successful;
}
public void setSuccessful(boolean successful) {
this.successful = successful;
}
#Override
public TransferResult marshal(ITransferResult v) throws Exception {
return (TransferResult) v;
}
#Override
public ITransferResult unmarshal(TransferResult v) throws Exception {
return (ITransferResult) v;
}
}
When I publish my web service, I get the following error:
Exception in thread "main" javax.xml.ws.WebServiceException: Unable to
create JAXBContext...
Caused by: java.security.PrivilegedActionException:
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1
counts of IllegalAnnotationExceptions ITransferResult is an interface,
and JAXB can't handle interfaces. this problem is related to the
following location: at ITransferResult
I've looked over SO for the answer and applied to the most repetitive tips, but none of them have worked for me yet.
What am I missing?
You may need to change the style to be DOCUMENT instead of RPC in your declaration at #SOAPBinding(style = Style.RPC)
Although this is an old question, I thought I'd answer it as it's common exception people encounter.
The difference between the two styles in high level is as follows
Document: The return type and method arguments are clearly explained in a separate XSD with each type in detail - helpful in case of custom data types (Example in your case ITransferResult or java.util.List).
RPC: the types are defined in the WSDL itself in simple manner.
It looks like it's not processing the annotations on the TransferResult class as a bindable element. That means you probably need to add #XmlSeeAlso(TransferResult.class) to the interface (ITransferResult). You also need to put #XmlRootElement on the serialization-implementation (TransferResult) so that an actual XML document can be produced, and not just a type that you use in some other document. This is because when the JAX-WS implementation is creating the JAXB context that it uses internally, it only uses the argument and result types that you define on the service interface as arguments to JAXB.newInstance(…); anything not literally listed there (or findable via simple following the types) will be omitted, and it's entirely possible that the type adapters used are not processed for annotations (after all, they don't need to be instances of the interface they're adapting, nor does the type being adapted need to be an interface).
(Yes, a SOAP response is an enclosing document, but the recommended way of using it is to put a single element inside the SOAP Body, and that means you need to know the name of the element. Which means an #XmlRootElement annotation.)
Warning: I'm not 100% sure that this will work. If it doesn't, you'll have to switch to using concrete types (probably straight POJOs) as results. It might not be a particularly palatable thing, but it's at least easy to do…
Consider the following interface/object hierarchy in a spring project:
public interface MyInterface {
//method defenitions
}
#Component
#Scope(SCOPE_PROTOTYPE)
public class MyClass implements MyInterface {
//method implementations
}
I use MyClass in a controller method where it is read from the requests body:
#RequestMapping(method = POST, value = "/posturi", consumes = "application/json")
public void createEntity(#RequestBody MyClass myClass) {
//handle request
}
The jackson library is used to read json data and convert it to a java object.
I would like to change the type of the parameter in the controller method from MyClass to MyInterface. This does not seem to work since the interface can't be instantiated with the new operator. But it could be created like this:
MyInterface instance = applicationContext.getBean(MyInterface.class);
Is it possible to make spring/jackson instantiate the object this way? I would like to do this so that my controller does not need to be aware of what implementation is used.
It should be possible with Converters. See documentation http://static.springsource.org/spring/docs/3.2.x/spring-framework-reference/html/validation.html. Question is, how do you know which class you return by converter? Rather rethink your design to use POJOs in input.
I have solved this now and the concept is quite simple but the implementation can be a bit tricky. As I understand it, you can annotate any type with #RequestBody as long as you provide a HttpMessageConverter that can convert from a http request to your desired type.
So the solution is:
Implement a HttpMessageConverter
Configure spring so that your HttpMessageConverter is used.
The second part can be a bit tricky. This is because spring adds a bunch of default HttpMessageConverter that can handle common types such as strings, integers, dates and I want these to continue to function as usual. Another problem is that if jackson is on the path, spring also adds a MappingJackson2HttpMessageConverter for generic json handling such as converting to concrete objects, maps and so on. Spring will use the first HttpMessageConverter it finds that claims to be able to convert to your type. The MappingJackson2HttpMessageConverter claims to be able to do so for my objects, but it is not able to, so it fails and the request fails. This could be considered a bug...
The chain that I wanted was:
Springs default HttpMessageConverters.
My own HttpMessageConverter
The MappingJackson2HttpMessageConverter
I found two ways to acheive this. First, you can declare this explicitly through xml.
<mvc:annotation-driven>
<mvc:message-converters>
<!-- All converters in specific order here -->
</mvc:message-converters>
</mvc:annotation-driven>
The downside of this is that if the default HttpMessageConverter chain changes in later releases, it will not change for your configuration.
Another way to do it is to programatically insert your own HttpMessageConverter before the MappingJackson2HttpMessageConverter.
#Configuration
public class MyConfiguration {
#Autowired
private RequestMappingHandlerAdapter adapter;
#Autowired
private MyHttpMessageConverter myHttpMessageConverter;
#PostConstruct
private void modify() {
List<HttpMessageConverter<?>> messageConverters = adapter.getMessageConverters();
int insertLocation = messageConverters.size() - 1;
for (int i = 0; i < messageConverters.size(); i++) {
Object messageConverter = messageConverters.get(i);
if (messageConverter instanceof MappingJackson2HttpMessageConverter) {
insertLocation = i;
}
}
messageConverters.add(insertLocation, myHttpMessageConverter);
}
}
The second alternative will continue to use the "default configuration" even if it changes in later releases. I consider it a bit hacky and not at all elegant but the reason I think it is a valid soulution is that there seems to be flaws in the MappingJackson2HttpMessageConverter claiming to be able to convert to types it cannot convert to. And also that you cannot explicitly add a HttpMessageConverter to a specific position in the chain.
For now I am going with the second option but how you do is up to you...
I have a bunch of third-party Java classes that use different property names for what are essentially the same property:
public class Foo {
public String getReferenceID();
public void setReferenceID(String id);
public String getFilename();
public void setFilename(String fileName);
}
public class Bar {
public String getRefID();
public void setRefID(String id);
public String getFileName();
public void setFileName(String fileName);
}
I'd like to be able to address these in a canonicalized form, so that I can treat them polymorphically, and so that I can do stuff with Apache BeanUtils like:
PropertyUtils.copyProperties(object1,object2);
Clearly it would be trivial to write an Adapter for each class ...
public class CanonicalizedBar implements CanonicalizedBazBean {
public String getReferenceID() {
return this.delegate.getRefID();
}
// etc.
}
But I wonder is there something out there more generalized and dynamic? Something that would take a one-to-many map of property name equivalences, and a delegate class, and produce the Adapter?
I've never used it, but I think you're looking for Dozer:
Dozer is a Java Bean to Java Bean mapper that recursively copies data
from one object to another. Typically, these Java Beans will be of
different complex types.
Dozer supports simple property mapping, complex type mapping,
bi-directional mapping, implicit-explicit mapping, as well as
recursive mapping. This includes mapping collection attributes that
also need mapping at the element level.
Dozer not only supports mapping between attribute names, but also
automatically converting between types. Most conversion scenarios are
supported out of the box, but Dozer also allows you to specify custom
conversions via XML.
First Option is Dozer.
Second option is Smooks Framework
with a tweak. It will be beneficial to use Smook's Graphical mapper.
Another option would be XStream with custom Mapper.
maybe something like that:
public class CanonicalizedBar implements CanonicalizedBazBean {
public String getReferenceID() {
Method m = this.delegate.getClass().getDeclaredMethod("getReferenceID");
if(m == null)
m = this.delegate.getClass().getDeclaredMethod("getRefID");
...
return m.invoke();
}
// etc.
}
Although, I personally have never used it. I noticed that a project called orika is noted as having the best performance and the ability to automatically understand many such mappings.
At any rate it also supports custom mappings and uses generated code to implicitly define the adapters.
You can also define a custom mapper, that is if you know how to canonize the member names you can use that knowledge to build a mapping that is true for all your objects. for instance:
DefaultFieldMapper myDefaultMapper = new DefaultFieldMapper() {
public String suggestMapping(String propertyName, Type<?> fromPropertyType) {
// split word according to camel case (apache commons lang)
String[] words= StringUtils.splitByCharacterTypeCamelCase(propertyName);
if(words[0].length() > 6) {
// trim first camel-cased word of propery name to 3 letters
words[0]= words[0].substring(0,2);
return StringUtils.join(words);
} else {
// remains unchanged
return propertyName;
}
}
}
mapperFactory.registerDefaultFieldMapper(myDefaultMapper );
I haven't done much with it but you may be able to use Aspect Oriented Programming to do this.
What you should be able to do I think is add a method to each of the classes that internally calls the real method. See this article about half way down it talks about mixins.
AspectJ is probably the most popular implementation.
I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info