Does the Play Framework have a Native or Recommended way of converting Play models into XML/JSON? Something similar to JAXB or Jackson.
Some people recommend the template approach but this is very verbose and doesn't guarantee well-formed XML/JSON.
The Play Documentation on XML just shows an XML response being built using String concatenation like so:
return ok("<message \"status\"=\"OK\">Hello " + name + "</message>");
Similarly, the Play Documentation on JSON shows a JSON object being built up one line at a time.
ObjectNode result = Json.newObject();
result.put("status", "OK");
result.put("message", "Hello " + name);
Is there a Standard way of serializing Models into XML/JSON using Play?
Is there any Official Play Documentation on this subject?
Short answer: Jackson for JSON and JAXB for XML
Play itself doesn't provide any documentation on marshalling models but it does ship with 3rd party libraries that can do the job.
JSON:
The model:
public class User extends Model {
public String username;
public Long age;
@JsonIgnore
public String password; // field won't be marshalled
}
Marshall it to JSON using jackson's ObjectMapper.writeValueAsString() method.
import org.codehaus.jackson.map.ObjectMapper;
//
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper.writeValueAsString(country);
JSON Output:
{
"username" : "John Smith",
"age" : "25"
}
XML:
Care must be taken because of how Play generates getters and setters for it's models under the hood. You won't see the getter and setters in the code but they exist at runtime.
On the model, it's important to set the XmlAccessorType annotation to PROPERTY. This tells JAXB to serialize from the getter/setters and not from the underlying fields.
@XmlAccessorType(XmlAccessType.PROPERTY)
We also have to add an @XmlRootElement annotation which specifies the name of the root XML node:
@XmlRootElement(name = "UserRoot")
To omit a field, we must add the @XmlTransient annotation to the getter. Since there is no getter in the source code, we must add one for every field we want to omit.
@XmlAccessorType(XmlAccessType.PROPERTY)
public class User extends Model {
public String username;
public Long age;
@JsonIgnore
public String password;
@XmlTransient // This means ignore this property
public String getPassword() {
return this.password;
}
}
The marshalling is performed by the JAXB classes Marshaller and JAXBContext
JAXBContext context = JAXBContext.newInstance(User.class);
Marshaller marshaller = context.createMarshaller();
// Use linefeeds and indentation in the outputted XML
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
marshaller.marshal(user, System.out);
Output:
<UserRoot>
<name>John Smith</name>
<age>25</age>
</UserRoot>
Summary:
The Play docs on XML and the Play docs on JSON do provide some information on working with json/xml but there doesn't seem to be any Play Docs describing how to do Marshalling. For that we have to look at 3rd Party libraries and documentation.
For JSON I'd suggest using... org.codehaus.jackson
, as it available as play.libs.Json
in Play 2/x
@see: Json Doc
For XML
- template approach is fair enough, as you can render proper XML with the view.
Edit:
Json and Ebean
Sadly must to say that Ebean has a problems with serializing its objects to JSON, therefore I'm always using dedicated inner class (in the target model, which contains only fields, that should be send in Json) ie, for User
model:
public static class ForJson {
public Long id;
public String name;
public String email;
public ForJson(User user) {
this.id = user.id;
this.name = user.name;
this.email=user.email;
}
}
routes:
GET /users/all.json controllers.Application.listUsersJson
GET /users/all-details.json controllers.Application.listUsersJsonWithDetails
GET /users/:id.json controllers.Application.singleUserJson(id: Long)
actions:
public static Result listUsersJson() {
List<User.ForJson> usersToJson = new ArrayList<>();
for (User user : User.find.all()) {
usersToJson.add(new User.ForJson(user));
}
return ok(Json.toJson(usersToJson));
}
public static Result singleUserJson(Long id) {
User.ForJson userForJson = new User.ForJson(User.find.byId(id));
return ok(Json.toJson(userForJson));
}
public static Result listUsersJsonWithDetails() {
Map<String, Object> details = new LinkedHashMap<>();
List<User.ForJson> usersToJson = new ArrayList<>();
for (User user : User.find.all()) {
usersToJson.add(new User.ForJson(user));
}
details.put("date", new Date());
details.put("count", usersToJson.size());
details.put("users", usersToJson);
return ok(Json.toJson(details));
}
Yes, I know maybe it's reduntand coding, but I have at least always proper JSON output, and I don't need to create JSON line by line in each action..
XML:
HTML chars won't break the rendering the proper XML as by default Play's templates escapes them, so instead of <
, >
, "
it will use <
, >
, "
inside the XML node:
<sample>Say "ellou"<sample>
Check Escaping paragraph in templates's doc (bottom of the page).
What's more you can use partial templates - tags to make sure, that single item will be formatted exactly the same in both: users/1.xml
and users/all.xml
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With