Marshalling nested objects with JAXB - unwrapped - java

I've got a very simple problem here, but after using Google for over an hour now, I still cannot find a good solution and it starts to cost too much money..
In my app I use REST as an API, basically only with JSON as payload type, and Enunciate for documentation of the API. As you might know, enunciate will generate an xsd schema from the classes. I am therefore in the situation, that I need to configure all the DTO's for Jackson/JSON handling with the suitable annotations, and also with JAXB annotation to ensure the generated schema is correct and can be parsed with XJC into the correct classes!
Although that is not very difficult to achieve and works flawless in most of the cases, I have a simple but somehow special case, in which it completely fails.
Assuming the following classes:
#JsonRootName(value = "location")
public class Location {
private String label;
#JsonUnwrapped
private Address address;
// constructors, getters, setters ommited..
}
//// new file
public class Address{
private String city;
private String street;
private String postCode;
}
This works 100% with Jackson/JSON. The embedded Address object will be unwrapped so that the JSON looks like this:
{
"label":"blah",
"street":"Exchange Road",
"city":"Stacktown"
"postCode":"1337"
}
This works forth and back with Jackson.
JAXB on the other hand is able to parse (most of) the Jackson annotations, so that generally you wont have problems with using simple objects in both worlds. #JsonUnwrapped though sadly is NOT supported by JAXB, and strangely that (from my POV) quite simple usecase seems to be not reflectable with any JAXB annotation at all.
What happens is, that the generated schema contains the embedded Address object, without any attributes/elements. Therefore the class generated by XJC will contain that Address object reference. And this ultimately leads to erroneous JSON from a app that will use the schema to generate objects...
Any ideas?

The JAXB (JSR-222) specification does not define an equivalent to Jackson's #JsonUnwrapped. For a long time we have offered that functionality in the EclipseLink MOXy implementation of JAXB via our #XmlPath extension.
#XmlPath(".")
private Address address;
For More Information
I have written more about MOXy's #XmlPath extension on my blog:
http://blog.bdoughan.com/2010/07/xpath-based-mapping.html

Related

Is there a Jackson annotation to use a wrapper class during deserialization as well as during serialization for Strings

Hi StackOverflow Community,
I am currently trying to deserialize JSON request bodies provided via Spring Boot #RestController.
The request body contains the following array:
{
...
"productIds": [
"123abc",
"234def"
],
...
}
However, I don't want to deserialize the product IDs into a list of Strings, but rather use a simple wrapper class (for various reasons, including but not limited to additional type safety and validation opportunities). Consequently the class looks like this (Lombok annotations were used to keep the code snippet short):
#Value
#AllArgsConstructor
public class TheRequest {
...
List<ProductId> productIds;
...
}
with ProductId being just a simple wrapper as already said (validation annotations are omitted for the sake of brevity):
#Value
#AllArgsConstructor
public class ProductId{
String id;
}
Looking at Stackoverflow I only found ways to achieve this using rather verbose custom deserialization methods.
However, I am a bit astonished, that Jackson does not provide this functionality out of the box. Consequently it would be great if anyone has any idea if
there is a more elegant way to achieve deserialization of a array of Strings into a List of WrapperObjects, ideally only using Jackson annotations?
there is an elegant way to achieve serialization of such a resulting List of ProductId wrapper objects back into String objects, ideally also using only Jackson annotations? I tried Jacksons #Value but that did not provide the required result.
To me still to verbose but it seems to be a working solution with Jacson 2.14+:
public record PayloadId(String id) {
#JsonCreator(mode = Mode.DELEGATING)
public PayloadId{}
#JsonValue
#Override
public String id() {
return id;
}
}
...and here is the records test https://github.com/FasterXML/jackson-databind/blob/2.14/src/test-jdk14/java/com/fasterxml/jackson/databind/records/RecordCreatorsTest.java

How to JSON parse immutable object in Java + Jersey

So I am just trying out Jersey for REST services and it seems to we working out fine. I only expose get services and all of the object types that I expose with these services have an immutable object representation in Java. By default Jersey seems to use a parser (JAXB?), requiring a #XmlRootElement annotation for the class that should be parsed, zero-arg constructor and setters.
I have been using Gson with no zero-arg constructor, no setters and final on all fields with no problems at all. Is there any way to accomplish this with Jersey(i.e. the paser it is using)? I have seen solutions with adapter classes that map data from a immutable object to a mutable representation, but this seems like a lot of boilerplate(new classes, more annotations, etc.) if it can be achieved with Gson without anything added.
Note: 1) I have heard people promote using zero-arg constructor and claim that Gson should not work without it. This is not what I am interested in. 2) I really have tried googling this but my keywords might be off. In other words, humiliate me in moderation.
EDIT 1:
My webservice works if I do like this:
#XmlRootElement
public class Code{
private String code; //Silly object just used for example.
public Code(){}
//(G || S)etters
}
With this class exposing the object:
#GET
#Produces(MediaType.APPLICATION_JSON)
public Set<Code> get(#QueryParam("name") String name) { // Here I want to use a class of my own instead of String name, haven't figured out how yet.
return this.codeService.get(name);
}
If I replace the Code with the following, the webservice stops working:
public class Code{
private final String code;
#JsonCreator
public Code(#JsonProperty("code") String code) {
this.code = code;
}
//Getters omitted
}
What I want is to be able to 1) have immutable objects that can be parsed to/from json and 2) Be able to define something like #RequestBody in Spring MVC for my incoming objects.
Actually this could be pretty easy with Genson. You just need the jar and then configure the Genson feature to use constructors with arguments (if you don't want to put annotations on it).
Genson genson = new GensonBuilder().useConstructorWithArguments(true).create();
// and then register it with jersey
new ResourceConfig().register(new GensonJaxRSFeature().use(genson));
Or you can use JsonProperty on the arguments. See the User Guide for more details.

FlexJson and #JSON annotation - #JSON(name = "") not working

I just switched to FlexJson and I am already having a problem.
The documentation at http://flexjson.sourceforge.net/, chapter Controlling JSON naming with #JSON states :
When an object is serialized the names of the properties on JSON
objects are determined from the name of the Java property. This means
the names of the getter methods or the field names are used. However,
sometimes the JSON output doesn't match the names used by the Java
object. You can control this using the #JSON annotation. For example:
public class Species {
#JSON(jsonName = "genus")
private String type;
private String name;
#JSON(jsonName="species")
public String getName() {
return name;
}
}
Except it doesn't work. And then they say :
Defining a #JSON.jsonName is used in both serialization and
deserialization.
Now when I have a look into the javadocs at http://flexjson.sourceforge.net/javadoc/index.html, I can see there are 4 optional elements belonging to the #JSON annotation, those are
include name objectFactory transformer
None of it is jsonName, like in the example.
So how do I get this annotation to work, so I can have different java and json names?
How can I define this annotation element or make use of the predefined name
To clarify, I can annotate #JSON and autocomplete recommends #JSON(include), but then include cannot be resolved...
I am using FlexJson 2.1, and I imported flexjson.JSON;
Btw I am aware of this answer https://stackoverflow.com/a/8879616/2001247, but it's not what I want. I want to use annotations.
You need to use FlexJson 3.2
The problem is, latest jar accessible via developer site have 2.1 version number.
But java doc corresponds to version 3.2.
You can find FlexJson 3.2 jar in maven repository.

How to ignore/disable/revert/override JAXB class-level #XmlJavaTypeAdapter in certain situations?

Our model classes are annotated with #XmlJavaTypeAdapter (at the class-level). Unmarshalling works fine for the root element and containment/nesting (according to what we implemented in our custom XmlAdapter).
So far, we were happy campers for both XML and JSON serialization/deserialization. However, a new need arose and I can't figure out how to implement it ?
In certain situations, I'd like to be able to "revert" to default JAXB behavior for containment: I want the class-level #XmlJavaTypeAdapter annotation to be ignored/overriden.
I spent hours reading Blaise Doughan's blog (http://blog.bdoughan.com/) and searching StackOverflow and Google but can't find an elegant/pragmatic solution.
Here is a quick setup to illustrate what we currently have (please note that all our JPA/Hibernate/other annotations are not listed for simplicity-sake but they do exist in our model classes (POJOs)):
Class Master
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#XmlJavaTypeAdapter(XmlMasterAdapter.class)
public class Master {
#XmlElement
private Long masterPrimaryKey;
#XmlElement
private String name;
}
Class Detail
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
#XmlJavaTypeAdapter(XmlDetailAdapter.class)
public class Detail {
#XmlElement
private Long detailPrimaryKey;
#XmlElement
private Master master; // reference/foreign key. No need for #XmlJavaTypeAdapter since it's defined at the class-level in Master.
#XmlElement
private String value;
}
When Master is used as a the root element, the XML is like this:
<master>
<masterPrimaryKey>1234</masterPrimaryKey>
<name>master name</name>
</master>
When Master is used as a contained/nested element, the XML is like this: (thanks to our custom XmlAdapter, the <master> element is "summarized" by its primary key)
<detail>
<detailPrimaryKey>5678</detailPrimaryKey>
<master>1234</master>
<value>detail value</value>
</detail>
So far, everything works fine and we're happy with it.
Now, our new need:
I'd like containment to work in a different way in specific situations.
I want the class-level #XmlJavaTypeAdapter on Master to "temporarily" be ignored/reverted/overridden in a specific context. I'd expect the default JAXB unmarshaller to kick-in (as if there had never been a class-level #XmlJavaTypeAdapter on the contained classes).
Think about a data-import situation where we receive the master and all the details in one payload. As if they were all independent root elements wrapped in a big DTO/transport container.
Here is the XML presenting what we want:
<masterDetailImport>
<master>
<!-- Primary keys omitted because of the import mode -->
<name>master name</name>
</master>
<details>
<detail>
<value>detail 1 value</value>
</detail>
<detail>
<value>detail 2 value</value>
</detail>
<detail>
<value>detail 3 value</value>
</detail>
</details>
</masterDetailImport>
Class MasterDetailImport
#XmlRootElement
#XmlAccessorType(XmlAccessType.NONE)
public class MasterDetailImport implements Serializable
{
#XmlElement
#PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
private Master master;
#XmlElementWrapper(name="details")
#XmlElement
#PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT
private List<Detail> detail = new ArrayList<Detail>();
}
What I'm looking for is the magic [yet non-existing] #PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT annotation that would allow me to instruct JAXB to do as if #XmlJavaTypeAdapter had never been defined at the class-level for the nested classes.
So far, the solutions we envisioned [and don't like] are:
Create "mirror" DTO objects for deserialization only when we must support import. The are many cons with this approach (duplicate code only used for deserialization, adapters to copy the DTO content into the model class, more unit tests to write/maintain, etc).
Get rid of class-level #XmlJavaTypeAdapter on all our entities we want to be able to import/nest and explicitly use #XmlJavaTypeAdapter on all attributes where nesting/containment is used. I tested this approach and know it would work. However, I think it's error prone and not as elegant as defining it at class-level and be able to have an exception/special-case/override handling telling JAXB to temporarily behave as if it never knew #XmlJavaTypeAdapter has been defined on the class.
I'm running out of ideas here... I tried looking for JAXB's default XML adapter but was not successful: javax.xml.bind.annotation.adapters.XmlAdapter<ValueType,BoundType> is abstract and inherits from Java.lang.Object.
Now, the simple question:
How to implement #PLEASE_IGNORE_CLASS_LEVEL_XmlJavaTypeAdapter_AND_UNMARSHAL_AS_IF_IT_WERE_A_ROOT_ELEMENT ?
Thanks in advance !
An XmlAdapter in JAXB will always be applied, but you can put logic in the XmlAdapter itself to handle your use case. By default a new instance of XmlAdapter will be created each time it will be used, if your XmlAdapter is stateful you can set an instance on the Marshaller or Unmarshaller so that it will be used instead. You can leverage this to help determine if it should be applied or not.
Below is a link to an answer I gave to a related question where a stateful XmlAdapter is used to inline an object the first time it is reference, and then marshal it as a link each subsequent time it is referenced.
Can JAXB marshal by containment at first then marshal by #XmlIDREF for subsequent references?

How do you marshall a parameterized type with JAX-WS / JAXB?

Consider the following classes (please assume public getter and setter methods for the private fields).
// contains a bunch of properties
public abstract class Person { private String name; }
// adds some properties specific to teachers
public class Teacher extends Person { private int salary; }
// adds some properties specific to students
public class Student extends Person { private String course; }
// adds some properties that apply to an entire group of people
public class Result<T extends Person> {
private List<T> group;
private String city;
// ...
}
We might have the following web service implementation annotated as follows:
#WebService
public class PersonService {
#WebMethod
public Result<Teacher> getTeachers() { ... }
#WebMethod
public Result<Student> getStudents() { ... }
}
The problem is that JAXB appears to marshall the Result object as a Result<Person> instead of the concrete type. So the Result returned by getTeachers() is serialized as containing a List<Person> instead of List<Teacher>, and the same for getStudents(), mutatis mutandis.
Is this the expected behavior? Do I need to use #XmlSeeAlso on Person?
Thanks!
LES
The answer to this one was rather tricky. It turns out that the version of jettison used by the jax-ws json plugin is a bit old (1.0-beta-1 IIRC). That particular version doesn't handle this case well (it crashes). If you do add the #XmlSeeAlso then the JSON marshalling will crash! Of course, this sucks!
I read on some forum (don't have the link - this is all from memory) that the jax-ws json plugin isn't actively maintained. If you try to 1) exclude the default jettison dependency from the jax-ws json depedency (assuming maven here) and add a newer version you get an error about JSONException not existing. Jax-ws json will NOT work with a newer version of jettison (I tried).
This is documented on another website (someone stated that they would like for someone to port jax-ws json to the latest jettison).
In the end, I switched to DWR for remoting. JAX-WS is best left for system-to-system (backend) integration. It's too heavy-weight for front-end stuff. DWR works AWESOMELY in this case.
Found link to forum I read: http://forums.java.net/jive/thread.jspa?messageID=384385 . Note that Collab.net is currently down for maintenance but they'll be back up soon.
As far as answering the general question (without the JAX-WS JSON plugin part), the answer is yes - you must use the #XmlSeeAlso annotation on the Person class. Otherwise the schema will only contain Person and not Teacher or Student elements. Only the elements defined in Person are marshalled without the #XmlSeeAlso annotation.

Categories