7

First off, before you decide to close my question, I've tried this solution, but it's not working for me.

I have a REST service that is supposed to return JSON or XML depending on the Accept header. I can have it generate proper JSON, but not XML. When I fix the XML the JSON gets screwed. Below I'm presenting my code.

XML seems good, but JSON not

Message.java

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElementRef;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Message {
    int id;
    String text;

    @XmlElementWrapper
    @XmlElementRef
    List<Comment> comments;

    public Message() {

    }
    // getters and setters
}

Comment.java

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name = "comment")
public class Comment {
    int id;
    String text;

    public Comment() {

    }
    //getters and setters
}

MessageResource.java

import java.util.List;

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;


@Path("messages")
public class MessageResource {

    DBUtils db = new DBUtils();

    @GET
    @Produces(MediaType.APPLICATION_XML)
    public Response getXML() {
        List<Message> messages = db.getMessages();
        return Response.ok(messages.toArray(new Message[messages.size()]), MediaType.APPLICATION_XML).build();
    }

    @GET
    @Produces(MediaType.APPLICATION_JSON)
    public Response getJSON() {
        List<Message> messages = db.getMessages();
        return Response.ok(messages.toArray(new Message[messages.size()]), MediaType.APPLICATION_JSON).build();
    }
}

Here's the XML result, which is OK:

<messages>
    <message>
        <id>1</id>
        <text>Java is an OOP language.</text>
        <comments>
            <comment>
                <id>20</id>
                <text>That's correct.</text>
            </comment>
            <comment>
                <id>30</id>
                <text>test test</text>
            </comment>
        </comments>
    </message>
    <message>
        <id>1</id>
        <text>Java is an OOP language.</text>
        <comments>
            <comment>
                <id>20</id>
                <text>That's correct.</text>
            </comment>
            <comment>
                <id>30</id>
                <text>test test.</text>
            </comment>
        </comments>
    </message>
</messages>

And here's the JSON result, pay attention to the comments. All I need is a comments array.

[
  {
    "id": 1,
    "text": "Java is an OOP language.",
    "comments": {
      "comment": [
        {
          "id": 20,
          "text": "That's correct."
        },
        {
          "id": 30,
          "text": "test test"
        }
      ]
    }
  },
  {
    "id": 1,
    "text": "Java is an OOP language.",
    "comments": {
      "comment": [
        {
          "id": 20,
          "text": "That's correct."
        },
        {
          "id": 30,
          "text": "test test."
        }
      ]
    }
  }
]

Fixing the JSON messes up the XML response

If I remove the @XmlElementWrapper and @XmlElementRef annotations from the Message class, then it works for JSON, but not XML.

Message.jave

import java.util.List;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Message {
    int id;
    String text;

    List<Comment> comments;

    public Message() {

    }
   //getters and setters
}

The Comment and MessageResource classes remain the same.

Here's the results I get:

JSON - OK

[
  {
    "id": 1,
    "text": "Java is an OOP language.",
    "comments": [
      {
        "id": 20,
        "text": "That's correct."
      },
      {
        "id": 30,
        "text": "test test"
      }
    ]
  },
  {
    "id": 1,
    "text": "Java is an OOP language.",
    "comments": [
      {
        "id": 20,
        "text": "That's correct."
      },
      {
        "id": 30,
        "text": "test test."
      }
    ]
  }
]

XML - WRONG

<messages>
    <message>
        <id>1</id>
        <text>Java is an OOP language.</text>
        <comments>
            <id>20</id>
            <text>That's correct.</text>
        </comments>
        <comments>
            <id>30</id>
            <text>test test</text>
        </comments>
    </message>
    <message>
        <id>1</id>
        <text>Java is an OOP language.</text>
        <comments>
            <id>20</id>
            <text>That's correct.</text>
        </comments>
        <comments>
            <id>30</id>
            <text>test test.</text>
        </comments>
    </message>
</messages>

Does anyone know how to make these two work together? The only solution I found to this is using JAXB for XML and GSON for JSON, but I have to manually create JSON objects using GSON.

Thanks!

Community
  • 1
  • 1
Turik Mirash
  • 201
  • 2
  • 15
  • Have you looked in to [JacksonJaxbJSON](http://fasterxml.github.io/jackson-jaxrs-json-provider/javadoc/2.0.1/com/fasterxml/jackson/jaxrs/json/JacksonJaxbJsonProvider.html) annotations ? like `JsonType`.. – ulab May 02 '17 at 14:37
  • @ulab, can you show me an example of how to use it? I haven't seen it before. – Turik Mirash May 03 '17 at 05:44

1 Answers1

6

My proposed solution uses JAXB for XML (like you). But for JSON it uses Jackson-JAXRS (unlike you), as for example described in this answer. So, instead of using GSON you would need to use Jackson-JAXRS (for example from Maven).

To get the desired XML and JSON output, you need to tune the annotations of the List<Comment> comments property in your Message class.

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Message {
    int id;
    String text;

    @XmlElementWrapper(name="comments")
    @XmlElementRef
    @JsonUnwrapped
    List<Comment> comments;

    //getters and setters
}

By @XmlElementRef you get each Comment object written as a <comment> element. Finally, by @XmlElementWrapper(name="comments") you get all these wrapped in a <comments> element.

The XML output is:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<messages>
  <message>
    <id>1</id>
    <text>Java is an OOP language.</text>
    <comments>
      <comment>
        <id>20</id>
        <text>That's correct.</text>
      </comment>
      <comment>
        <id>30</id>
        <text>test test.</text>
      </comment>
    </comments>
  </message>
  <message>
    <id>1</id>
    <text>Java is an OOP language.</text>
    <comments>
      <comment>
        <id>20</id>
        <text>That's correct.</text>
      </comment>
      <comment>
        <id>30</id>
        <text>test test.</text>
      </comment>
    </comments>
  </message>
</messages>

By @JsonUnwrapped (imported from package com.fasterxml.jackson.annotation) you get the List<Comment> comments written as a plain array of objects.

The JSON output is:

[
  {
    "id": 1,
    "text": "Java is an OOP language.",
    "comments": [
      {
        "id": 20,
        "text": "That's correct."
      },
      {
        "id": 30,
        "text": "test test."
      }
    ]
  },
  {
    "id": 1,
    "text": "Java is an OOP language.",
    "comments": [
      {
        "id": 20,
        "text": "That's correct."
      },
      {
        "id": 30,
        "text": "test test."
      }
    ]
  }
]
Community
  • 1
  • 1
Thomas Fritsch
  • 9,639
  • 33
  • 37
  • 49
  • Thanks! Your solution is producing correct XML, and almost correct JSON, except that the array is name "comment" and not "comments". I had to change the XML annotation to make it work. I used these annotations `XMLElementWrapper(name="comments") @XMLElementRef @JsonUnrwapped`. – Turik Mirash May 10 '17 at 07:42
  • @TurikMirash Thanks for your feedback! I updated my answer with your improved XML annotations. – Thomas Fritsch May 10 '17 at 11:53