I have an object that uses a class instance as a wrapper for the String type. How would I serialize my class into a JSON object using JAX-RS and Java EE 6 by using only the toString() method without creating a JSON object for the class and each fields?
Class to be serialized:
public class Text {
private String content;
protected Text( String content ) {
this.content = content;
}
public static Text from( String content ) {
return new Text( "The text content");
}
// This is a method that is used by the subclass and has no meaning for serialization
protected String getContent() {
return content;
}
// This should be used to serialize this class into a String, instead of an object
@Override
public String toString() {
return content;
}
}
Parent class that uses the class to be serialized:
public class EditorStoryChapterInput {
private Text title;
public Text getTitle() {
return title;
}
}
Result expected: I want to serialize as { "title": "The text content" }
instead of { "title": { content: "the text content" } }
.
EDIT: Looking in the Jackson FAQ I found something useful, and what I want is probably the equivalent of what I found, but inside a JAX-RS/Java EE 6 environment in JBoss EAP 6.1. There is nothing as @JsonValue
in my classpath using JBoss EAP 6.1:
http://wiki.fasterxml.com/JacksonFAQ#Serializing_values_as_simple_types
From Comments:
I don't know about spec compliant, but I just tested it, adding the Jackson dependency to my project, and just putting the @JsonValue
over the toString()
like mentioned in the link, and it works fine. Like I said, JBoss is using Jackson under the hood to do the serializing. So there's nothing wrong with using it in your project. You just need to add the dependency in order to compile the annotation. You can get it straight from the server modules if you need to do. But it'll be easier to just use Maven
Here are the classes I used for the example. I'll post the classes, then I point out how to get the dependencies.
Your Text
class
/** Getter and Setters added for Jackson discovery **/
public class Text {
private String content;
public void setContent(String content) { this.content = content; }
protected String getContent() { return content; }
@Override @JsonValue
public String toString() { return content; }
}
Your EditorStoryChapterInput
class
public class EditorStoryChapterInput {
private Text title;
public void setTitle(Text title) { this.title = title; }
public Text getTitle() { return title; }
}
The resource class
@Path("/json")
public class JsonResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getJson() {
EditorStoryChapterInput input = new EditorStoryChapterInput();
Text title = new Text();
title.setContent("Hello World");
input.setTitle(title);
return Response.ok(input).build();
}
}
The Application
class to register out resource
@ApplicationPath("/rest")
public class JsonApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
Set<Class<?>> classes = new HashSet<Class<?>>();
classes.add(JsonResource.class);
return classes;
}
}
Result
Get the Dependencies
If you are using Maven, you just need the jackson-core-asl
artifact. When I use JBoss, I prefer to use their boms, so they can manage the versions. So What your pom would look something like (Remember I said we can use provide
scope, because JBoss already has these dependencies. We just need it to compile)
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.jboss.bom</groupId>
<artifactId>jboss-javaee-6.0-with-resteasy</artifactId>
<version>1.0.7.Final</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-core-asl</artifactId>
<scope>provided</scope>
</dependency>
... Other dependencies
</dependencies>
If you're not using Maven, you can simply look inside
${eap-home-dir}\modules\system\layers\base\org\codehaus\jackson\jackson-core-asl\main
There you will find the jackson-core-asl-1.9.9.redhat-3.jar
. Just add that to your project however you normally add jars to your project. Tested this approach, with same result
Let me know if you have any problems adding the dependency.
UPDATE: Without using Jackson (no third party dependencies)
We can use the Java API for JSON Processing, and implement our own MessageBodyWriter
, which will be used from writing the EditorStoryChapterInput
object to the response OutputStream
. It might look something like
@Provider
@Produces(MediaType.APPLICATION_JSON)
public class TextMessageBodyWriter
implements MessageBodyWriter<EditorStoryChapterInput> {
@Override
public boolean isWriteable(Class type, Type type1,
Annotation[] antns, MediaType mt) {
return type == EditorStoryChapterInput.class;
}
@Override
public long getSize(EditorStoryChapterInput t, Class<?> type,
Type type1, Annotation[] antns, MediaType mt) {
return -1;
}
@Override
public void writeTo(EditorStoryChapterInput input, Class<?> type,
Type type1, Annotation[] antns, MediaType mt,
MultivaluedMap<String, Object> mm, OutputStream out)
throws IOException, WebApplicationException {
Text title = input.getTitle();
JsonObject jsonObject =
Json.createObjectBuilder()
.add("title", title.toString()).build();
try (JsonWriter jsonWriter = Json.createWriter(out)) {
jsonWriter.writeObject(jsonObject);
}
}
}
When searching for MessageBodyWriters
, JAX-RS will look at the @Produces
annotation to see what type it produces, and if it matches the @Produces
method on our resource method, the writer will be put into a list for the framework to traverse. Next it will check the isWritable
method on each of the writers. If it returns true, then that's the writer it will use. In our case, we return true if the return type of the response body is of EditorStoryChapterInput
type.
In the writeTo
method, we use the Java JSON Processing API to create a JsonObject
and write it to the OutputStream
provided to us by the framework.
Then we can register the writer with the application
@ApplicationPath("/rest")
public class TextApplication extends Application {
@Override
public Set<Class<?>> getClasses() {
final Set<Class<?>> classes = new HashSet<>();
classes.add(JsonResource.class);
classes.add(TextMessageBodyWriter.class);
return classes;
}
}
Testing it with the same JsonResource
class from above, we get the output
{"title":"Hello World"}
- Jersey has a good tutorial for MessageBodyWriters/MessageBodyReaders
- Resteasy's tutorial is not as well explained as Jersey's
- Also see Java EE's tutorial for JSON Processing