Hypermedia with Jersey using Atom - java

Every book on REST uses <atom:link href="..." rel="..."> to define Hypermedia in RESTful apps; but Jersey (with the use of JAXB) do not seems to have this support.
I've tried #XmlSchema in package-info.java as explained here; I've also tried extendingNamespacePrefixMapper as explained there. But none works and output this at best:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<customer xmlns:ns2="http://www.w3.org/2005/Atom">
<first-name>Roland</first-name>
<ns2:link href="/customer/1" rel="self" />
</customer>
Using namespace and, as a result, Atom, seems impossible in Jersey. I've miss something?
ps. I'me using a XSD to generate #XmlElement classes, and, for the moment, I create my own Link class. Is there a schema or a JAR to do that (jersey-atom maven dependency uses rome but without any help)

(Assuming that you are not concerned with the namespace prefix and just want to create the links)
Here is my approach to creating the links. In my resource class (the jersey rest service), I return a java object (below "Person"), whose class is decorated with jaxb annotations. One of the properties returns atom link objects.
#XmlRootElement(namespace = Namespace.MyNamespace)
public class Person implements Serializable {
private AtomLinks links = null;
#XmlElement(name = "link", namespace = Namespace.Atom)
public AtomLinks getLink() {
if (this.links == null) {
this.links = new AtomLinks();
}
return this.links;
}
..
}
#XmlAccessorType(value = XmlAccessType.NONE)
public class AtomLinks extends ArrayList<AtomLink> {
..
}
#XmlAccessorType(value = XmlAccessType.NONE)
public class AtomLink implements Serializable {
#XmlAttribute(name = "href")
public URI getHref() {
return href;
}
#XmlAttribute(name = "rel")
public String getRel() {
return rel;
}
#XmlAttribute(name = "type")
public String getType() {
return type;
}
#XmlAttribute(name = "hreflang")
public String getHreflang() {
return hreflang;
}
#XmlAttribute(name = "title")
public String getTitle() {
return title;
}
..
}
public class Namespace {
public final static String Atom = "http://www.w3.org/2005/Atom";
..
}
Prior to returning my object ("Person") I fill in the links, creating a self link and links to other related links. I utilize the uriInfo object that jersey injects to get the base url. If this is helpful but you would like more of the example, let me know and I will fill in the gaps.

If I am right there is an approach in Jersey to inject the links to the objects.
See: Jersey 2.9 User Guide Chapter 6.

Related

Java Reflection: Invoking Setter and Getter method for collection type Object

I have two different packages of User define Objects.....
1) ws.lender.dto (all Objects exists in this package are source side).
2) copl.com.dto (all Objects exists in this package are destination side).
Objects hierarchy and Objects name different in both side. I wan to
copy source side object to destination side object field by field or
via getter and setter using Reflection.
For Example
Source side Objects
package ws.lender.dto;
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "CustomerAddresses", propOrder = {
"previousAddresses"
})
public class CustomerAddresses {
protected PreviousAddresses previousAddresses;
public PreviousAddresses getPreviousAddresses() {
return previousAddresses;
}
public void setPreviousAddresses(PreviousAddresses value) {
this.previousAddresses = value;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "PreviousAddresses", propOrder = {
"previousAddress"
})
public class PreviousAddresses {
#XmlElement(name = "PreviousAddress", required = true)
protected List<PreviousAddress> previousAddress;
public List<PreviousAddress> getPreviousAddress() {
if (previousAddress == null) {
previousAddress = new ArrayList<PreviousAddress>();
}
return this.previousAddress;
}
}
#XmlAccessorType(XmlAccessType.FIELD)
#XmlType(name = "PreviousAddress", propOrder = {
"streetNo",
"streetName"
})
public class PreviousAddress {
#XmlElement(name = "StreetNo", required = true)
protected String streetNo;
#XmlElement(name = "StreetName", required = true)
protected String streetName;
public String getStreetNo() {
return streetNo;
}
public void setStreetNo(String value) {
this.streetNo = value;
}
public String getStreetName() {
return streetName;
}
public void setStreetName(String value) {
this.streetName = value;
}
}
Destination side Objects
package copl.com.dto;
#javax.persistence.Entity
public class Customer implements java.io.Serializable
{
private Set<CustomerAddress> customerAddresses;
public Set<CustomerAddress> getCustomerAddresses()
{
return customerAddresses;
}
public void setCustomerAddresses(Set<CustomerAddress> customerAddresses)
{
this.customerAddresses = customerAddresses;
}
}
#javax.persistence.Entity
public class CustomerAddress implements java.io.Serializable
{
private String unitNumber;
private String streetName;
private String streetNumber;
public String getUnitNumber()
{
return unitNumber;
}
public void setUnitNumber(String unitNumber)
{
this.unitNumber = unitNumber;
}
public String getStreetName()
{
return streetName;
}
public String getStreetNumber()
{
return streetNumber;
}
public void setStreetName(String streetName)
{
this.streetName = streetName;
}
public void setStreetNumber(String streetNumber)
{
this.streetNumber = streetNumber;
}
}
I think you could use MapStruct to mapping between POJO's that has different attribute names.
But your scenario is complex, because you want to convert ws.lender.dto.CustomerAddresses to copl.com.dto.Customer, and this implies to convert a List<ws.lender.dto.PreviousAddress> contained into a ws.lender.dto.PreviousAddresses object, to a Set<copl.com.dto.CustomerAddress> contained into a copl.com.dto.Customer object.
So, I will explain step by step.
1. Convert from ws.lender.dto.PreviousAddress to copl.com.dto.CustomerAddress
To do this conversion you need an interface (MapStruct will create an instance for this) responsible for mapping from source object to destination object:
import ws.lender.dto.PreviousAddress;
import copl.com.dto.CustomerAddress;
#Mapper
public interface CustomerAddressesMapper{
CustomerAddressesMapper INSTANCE = Mappers.getMapper( CustomerAddressesMapper.class );
#Mappings(#Mapping(source = "streetNo", target = "streetNumber")
CustomerAddress previousToCustomerObject(PreviousAddress address);
}
This interface will map a PreviousAddress object to a CustomerAddress considering that streetNo attribute has to be mapped to streetNumber. There is no mapping for unitNumber attribute cause there is no source for it.
2. Convert a List<ws.lender.dto.PreviousAddress> to Set<copl.com.dto.CustomerAddress>
Now you have to add another mapping method to existing CustomerAddressesMapper interface:
Set<CustomerAddress> previousToCustomerSet(List<PreviousAddress> addresses);
This method will use the former previousToCustomerObject to convert every element of source list to destination set.
3. Convert from ws.lender.dto.CustomerAddresses to copl.com.dto.Customer
Finally, you need to add the last mapping method to CustomerAddressesMapper interface:
#Mappings(#Mapping(source = "previousAddresses.previousAddress", target = "customerAddresses")
Customer customerAddrsToCustomerObject(CustomerAddresses addresses);
This is the where you map an origin object, converting previousAddresses.previousAddress attribute to customerAddresses attribute, using former methods.
4. Using the mapper
To use the mapper you have to write some code as following:
CustomerAddressesMapper mapper = CustomerAddressesMapper.INSTANCE;
CustomerAddresses origin = //Retrieve it from anywhere
Customer dest = mapper.customerAddrsToCustomerObject(origin);
5. Setup
MapStruct is a source code generator, so you need to properly configure your pom.xml to include MapStruct dependency and to invoke this code generation. You can see how to do this here
Well I don't build and run this code, but this is the way to do it.
Hope it helps!
I have studied many objects mapping frameworks for this assignments like
Orika
MapStruct
ModelMapper
Dozer
Commons-BeanUtils
Finally I choose the Orika framework to complete that above Objects to Objects mapping. We can do the that mapping via other mapper framework, but I was like Orika framework because this framework very easy to use for mapping Objects to Objects.
I will explain step by step.
1. Create the Objects of Source side Object and Destination side Object.
like this..
Customer destination = new Customer();
CustomerAddresses source = new CustomerAddresses();
source = filledCustomerAddressesObject();
2. Constructing the DefaultMapperFactory
MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();
3. Mapping fields
ClassMapBuilder<CustomerAddresses, Customer> builder;
builder= mapperFactory.classMap(CustomerAddresses.class, Customer.class).constructorA();
builder.field("previousAddresses.previousAddress{streetNo}","customerAddresses{streetNumber}");
builder.field("previousAddresses.previousAddress{streetName}","customerAddresses{streetName}");
builder.register();
BoundMapperFacade<CustomerAddresses, Customer> boundMapper;
boundMapper = mapperFactory.getMapperFacade(CustomerAddresses.class, Customer.class);
destination = boundMapper.map(source, destination);
Its work fine Cheers
You can try Object Mapper better for casting or copying. The object to other classes in other packages you can add some property value like
senderClass and rvcClass
Later you can read those properties and proceed for converting the class. Probably you already have mapping ready for sender class against receiver class.
If I understand you correctly, you need a way to copy all like-named properties from one object to another. Like-named properties would be cases in which the source object has a method called something like getPropertyName() and the destination object has one called setPropertyName().
If this is right, then you want to use the copyProperties method of the BeanUtils class from the Apache Commons library. Documentation is here.
Now, in your example, you have some corresponding properties that are not like-named, such as StreetNumber and StreetNo. I'm afraid there's no easy way to handle that sort of thing automatically through reflection; you would need to define the mappings between source and target properties yourself, perhaps by defining a helper class to do the copying.

how to set resource relations through annotations in Spring Hateoas?

Is there a way to set resource relations through annotations? I made a similar question a some time ago but i've not been clear enough. I want to have something like this:
public class UserResource {
private String username;
#Relation(value = "{servicebaseUrl}/classes/${value}", rel = "class")
private String classId;
// Getters and setters
}
And then add a message converter which would add links only if client sends Accept = application/hal+json, avoiding the fact of doing two different controller endpoints for application/hal+json and application/json. Does Spring offers something like that? I found that it actually offers this #Relation annotation(or similar one) but it seems that it is not for the same purposes.
No this is not possible - you would have to implement a ResourceAssembler to add links to your resources.
Usually your resources extend ResourceSupport.
class PersonResource extends ResourceSupport {
String firstname;
String lastname;
}
Then your create ResourceAssembler to control the creation of that resource:
class PersonResourceAssembler extends ResourceAssemblerSupport<Person, PersonResource> {
public PersonResourceAssembler() {
super(PersonController.class, PersonResource.class);
}
#Override
public PersonResource toResource(Person person) {
PersonResource resource = createResource(person);
// … do further mapping and add links
resource.add(new Link("http://myhost/people"));
return resource;
}
}
See the spring hateoas documentation for details

How can I use same JAXB class to marshall and unmarshall into different namespaces?

Note: this is with Java 7. Also, I am not using JAXB to generate anything from a schema. I only/merely want to use JAXB to deserialize pieces of existing SOAP messages. So the JAXB beans are "hand-written".
I am trying to deserialize the following XML with JAXB. I do not have access to it as a string. Rather, I am getting it as an org.w3c.dom.Element object:
<SynchInfo xmlns="urn:InitronSOAPService">
<HandshakeInfo>
<dnsName>foo.bar</dnsName>
<ip>1.2.3.4</ip>
<id>TheId</id>
<key>SomeKey</key>
<version>1.0</version>
</HandshakeInfo>
</SynchInfo>
But I would also like to marshall HandshakeInfo instances into a SOAPBody that is part of a message belonging to a different namespace.
I first tried to write a HandshakeInfo class that didn't mention namespaces at all:
#XmlRootElement(name = "HandshakeInfo")
#XmlAccessorType(XmlAccessType.NONE)
final private static class HandshakeInfo
{
private String version;
private String id;
private String key;
private String ip;
private String dnsName;
public String getVersion() {
return version;
}
#XmlElement(name = "version")
public void setVersion(String version) {
this.version = version;
}
public String getId() {
return id;
}
#XmlElement(name = "id")
public void setId(String id) {
this.id = id;
}
public String getKey() {
return key;
}
#XmlElement(name = "key")
public void setKey(String key) {
this.key = key;
}
public String getIp() {
return ip;
}
#XmlElement(name = "ip")
public void setIp(String ip) {
this.ip = ip;
}
public String getDnsName() {
return dnsName;
}
#XmlElement(name = "dnsName")
public void setDnsName(String dnsName) {
this.dnsName = dnsName;
}
}
But when I used that to try to deserialize the Element object containing the HandshakeInfo element, I got the following exception:
javax.xml.bind.UnmarshalException: unexpected element (uri:"urn:InitronSOAPService", local:"HandshakeInfo"). Expected elements are <{}HandshakeInfo>
at com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext.handleEvent(UnmarshallingContext.java:647)
...
So then I tried adding , namespace="urn:InitronSOAPService" to all the element and attribute (in this case none) annotations in the class. (Or, alternatively, used an #XmlSchema annotation in a package-info.java file.) That worked for deserializing that element in that namespace.
But when I tried to use the class to serialize into a different namespace I (not entirely unexpectedly) get stuff like this:
<SomeMessage xmlns="urn:differentNamespace">
<ns5:HandshakeInfo xmlns:ns5="urn:InitronSOAPService">
<ns5:dnsName>foo.bar</ns5:dnsName>
<ns5:ip>1.2.3.4</ns5:ip>
<ns5:id>TheId</ns5:id>
<ns5:key>SomeKey</ns5:key>
<ns5:version>1.0</ns5:version>
</ns5:HandshakeInfo>
</SomeMessage>
That's not what I want. I want the serialized thing to be part of the urn:differentNamespace.
So is there any way to use the same JAXB class in multiple namespaces? Or am I stuck with having to multiple copies of classes with each one annotated to be in the appropriate namespace?
I have seen some tricks using filters, etc. but those tricks seem to be assuming the XML is being read from or written to a stream or a file. Again, for unmarshalling I am working with org.w3c.dom.Element objects and for marshalling I am working with SOAPBody objects.
I see the following options:
Use MOXy XML Bindings and move mappings out of annotations into XML resources. You could then define and load different resources for different namespaces.
Use XSLT to preprocess XML prior to unmarshalling/postprocess after marshalling. Remapping namespaces is a trivial task in XSLT.
Use a custom AnnotationReader for JAXB RI to "fake" namespace read from annotations. You just have to adjust the namespace read from the package, should not be too hard.
AnnotationReader solution is a bit "hacky", also ties you to JAXB RI. XML bindings would require you to rewrite your annotations in XML form and will tie you to MOXy (I think). XSLT is quite elegant but will make the processing slower. And you'll have to hack into the processing chain with your XSLT.
All the options have pros and cons, you have to carefully consider them for your specific case.

XML Deserialization errors using Simple XML

In one of my applications I'm using thegamesdb.net's APIs to download the cover images of some games. To get informations from XML files I'm trying to implement Simple XML library to deserialize.
This is my example query result (name=Splatterhouse,platform=TurboGrafx 16):
<?xml version="1.0" encoding="UTF-8" ?>
<Data>
<Game>
<id>3776</id>
<GameTitle>Splatterhouse</GameTitle>
<ReleaseDate>01/01/1990</ReleaseDate>
<Platform>TurboGrafx 16</Platform>
</Game>
</Data>
And this is how I implemented my classes in java:
#Root
public class Data{
#ElementList(inline=true)
private List<Game> list; //This is correct, it's only in the example that I've only one result.
public List<Game> getGames(){
return list;
}
}
#Root
public class Game{
#Element
private int id;
#Element
private String GameTitle;
#Element(required=false)
private String ReleaseDate;
#Element(required=false)
private String Platform;
public String getTitle(){
return GameTitle;
}
public int getId(){
return id;
}
}
To read from the xml file, I call the serializer:
String xml = getXmlFromUrl(URL);
Serializer serializer = new Persister();
Data data = serializer.read(Data.class, xml);
What's wrong?
UPDATE: It came out that the tutorials on Simple Site are a bit incomplete since they don't set properly annotations, these annotations solved all my issues, plus all the warnings are "normal", it's simple stating that jaxp doesn't exist on android and is going to use other tools (XmlPullParser?).
This is my proper code:
#Root(name = "Data")
public class Data {
#ElementList(inline = true, required = false)
private List<Game> list = new ArrayList<Game>();
public List<Game> getGames() {
return list;
}
}
#Root(name = "Game", strict = false)
public class Game {
#Element(name = "id")
private int id;
#Element(name = "GameTitle")
private String GameTitle;
public String getTitle() {
return GameTitle;
}
public int getId() {
return id;
}
}
You could also use JAXB to (de)serialize XMLs (or to (un)marshal as they call it). It comes with java - so there's no need for an additional library. Here you have an example.
I believe you need to declare your classes in their own files (Data and Game), or mark them as static. If you declare them as inner classes they can't be constructed by the library because they can only exist within an instance of the parent class.
It's nothing to do with your XML, it seems like something is wrong with the library you're using (not being able to create a new instance of a certain class). Consider re-downloading it and adding it to your project again.
The tutorials on SimpleXML website show an incomplete and inaccurate use of annotations.
I updated my code, check at the bottom of my answer to get the proper implementation. You're strictly adviced to use name="Name" in your annotations, and if you don't need all the Elements/Attributes use strict=false on the class and required=false on your elements or attributes.

JaxB unmarshal custom xml

I'm currently attempting to unmarshal some existing XML into a few classes I have created by hand. Problem is, I always get an error that tells me, JaxB expects a weather element but finds a weather element. (?)
javax.xml.bind.UnmarshalException: unexpected element (uri:"http://www.aws.com/aws", local:"weather"). Expected elements are <{}api>,<{}location>,<{}weather>
I tried with and without "aws:" in the elements' name.
Here's my weather class:
#XmlRootElement(name = "aws:weather")
public class WeatherBugWeather
{
private WeatherBugApi api;
private List<WeatherBugLocation> locations;
private String uri;
#XmlElement(name="aws:api")
public WeatherBugApi getApi()
{
return this.api;
}
#XmlElementWrapper(name = "aws:locations")
#XmlElement(name = "aws:location")
public List<WeatherBugLocation> getLocations()
{
return this.locations;
}
public void setApi(WeatherBugApi api)
{
this.api = api;
}
public void setLocations(List<WeatherBugLocation> locations)
{
this.locations = locations;
}
#XmlAttribute(name="xmlns:aws")
public String getUri()
{
return this.uri;
}
public void setUri(String uri)
{
this.uri = uri;
}
}
And that's the XML I try to parse:
<?xml version="1.0" encoding="utf-8"?>
<aws:weather xmlns:aws="http://www.aws.com/aws">
<aws:api version="2.0" />
<aws:locations>
<aws:location cityname="Jena" statename="" countryname="Germany" zipcode="" citycode="59047" citytype="1" />
</aws:locations>
</aws:weather>
I'm not quite sure what I'm doing wrong. Any hints? I suspect the problem to be the xmlns definition, but I have no idea what to do about it. (You can see that by looking at the uri-property. That was one unsuccessful idea. ^^) And yes, I did try to set the namespace but that rather set's the namespace's uri instead of it's ... name.
I would recommend adding a package-info class in with your domain model with the #XmlSchema annotation to specify the namespace qualification:
package-info
#XmlSchema(
namespace = "http://www.aws.com/aws",
elementFormDefault = XmlNsForm.QUALIFIED)
package com.example.foo;
import javax.xml.bind.annotation.XmlNsForm;
import javax.xml.bind.annotation.XmlSchema;
Note
Your XmlRootElement and #XmlElement annotation should not contain the namespace prefix. You should have #XmlRootElement(name = "weather") instead of #XmlRootElement(name = "aws:weather")
For More Information
http://blog.bdoughan.com/2010/08/jaxb-namespaces.html
you need namespaces in your code. namespace prefixes are meaningless, you need the actual namespace (i.e. "http://www.aws.com/aws").
#XmlRootElement(name = "weather", namespace="http://www.aws.com/aws")

Categories

Resources