How to convert Play Framework Models into XML and JSON?
Asked Answered
F

3

12

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?

Foretopsail answered 5/2, 2013 at 18:35 Comment(0)
F
12

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.

Foretopsail answered 11/2, 2013 at 10:17 Comment(0)
B
2

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 &lt;, &gt, &quot; inside the XML node:

<sample>Say &quot;ellou&quot;<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

Bathurst answered 5/2, 2013 at 21:15 Comment(4)
Thanks biesior. The jackson docs builds the json one field at a time. What I'm looking for is a way to automatically convert a model into json in a single statement, using the java class name and java field names as the corresponding json identifiers. Is this possible with jackson? Do you know of any docs describing this? The problem with the XML template approach is that it doesn't always return well-formed XML. For example, if the data contained any special characters like < or > or ", these would break the xml at runtime. A generator that guarantees well-formedness is essential. ThanksForetopsail
I don't know formatter for XML, anyway still think that's possible to use templates, check my edit pls.Bathurst
Your comment here saved me, I think it's ridiculous that Play does not support an easy way to create a JSON Array!!! Why does it not support a put(ArrayList<>()) for a play.libs.Json.newObject()??? Thanks for your answer!Luciusluck
How about yourObjectNode.put("items", Json.toJson(yourArray)) ?Bathurst
M
1

There is a better way for JSON conversion.

User user = new User("...");
String jsonString = Ebean.json().toJson(user);
Mezzosoprano answered 26/7, 2016 at 5:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.