Reputation: 412
UPDATE: Ultimately I tried everything suggested here and seemed to have some progress but my API was over engineered for the intended purpose and I ran out of time, so I switched to hard coding "student-list" in the string response of my GET /student call which was more than enough for the proof of concept I needed it for.
I am working on a simple REST service that uses XML to work with student records.
when I do a GET on /student I get:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<students>
<student>
<grade>B</grade>
<name>Jane</name>
</student>
...
</students>
However, I would like the output to be:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student-list>
<student>
<grade>B</grade>
<name>Jane</name>
</student>
...
</student-list>
Here is my Student.java snippet:
@XmlRootElement(name = "student")
public class Student {
private String name;
private String grade;
public Student() {
}
public Student(String name, String grade) {
super();
this.name = name;
this.grade = grade;
}
// getters/setters ...
}
I have tried change the XmlRootElement to be "student-list", but that makes every sub element of the list a student-list type, and ignores the top level element list.
The project uses JAX-RS with a StudentsResource class which I have tried setting the XmlRootElement name on that resource class, but that did not affect any output.
It seems to be automatically naming the top level list based on the StudentsResource class, and I cannot figure out how to override this name.
I have been combing the documentation on this but Java is not my strongest language these days so I am having trouble understanding how the pieces fit together for JAXB and JAX-RS.
Any ideas, or a push in the right direction ?
EDIT: added StudentsResource.java snippet:
@Path("/student")
public class StudentsResource {
// this is a mapper class
@Context
UriInfo uriInfo;
@Context
Request request;
StudentService studentService;
public StudentsResource() {
studentService = new StudentService();
}
@GET
@Produces("text/xml;charset=utf-8")
public List<Student> getStudents() {
return studentService.getStudentAsList();
}
...
}
Based on Mateen Momin's feedback, I now have this outputting by the REST service on GET /student:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<studentDao_Students>
<student>
<name>Jane</name>
<grade>B</grade>
</student>
...
</studentDao_Students>
I have a StudentDao, which gets wrapped by a StudentService, which ultimately gets called by resource classes to make the HTTP calls possible. I have moved my student class into StudentDao, which made me realize that the StudentService is what is actually getting marshalled, here are updated snippets:
StudentDao.java:
public enum StudentDao {
instance;
private Map<String, Student> students = new HashMap<String, Student>();
private StudentDao() {
//pre-populate for testing purposes
Student student = new Student("John", "A");
students.put("John", student);
student = new Student("Jane", "B");
students.put("Jane", student);
student = new Student("TESTUSER", "F");
students.put("TESTUSER", student);
}
public Map<String, Student> getStudents() {
return students;
}
@XmlRootElement(name="student")
@XmlAccessorType(XmlAccessType.FIELD)
public static class Student {
private String name;
private String grade;
public Student() {
}
public Student(String name, String grade) {
super();
this.name = name;
this.grade = grade;
}
// getters/setters here...
}
}
StudentService.java:
@XmlRootElement(name = "student-list")
@XmlAccessorType(XmlAccessType.FIELD)
public class StudentService {
StudentDao studentDao;
public StudentService() {
studentDao = StudentDao.instance;
}
// CRUD methods here which call the StudentDao HashMap
}
StudentsResource.java:
@Path("/student")
public class StudentsResource {
@Context
UriInfo uriInfo;
@Context
Request request;
StudentService studentService;
public StudentsResource() {
studentService = new StudentService();
}
@GET
@Produces("text/xml;charset=utf-8")
public List<Student> getStudents() {
return studentService.getStudentAsList();
}
// other HTTP calls, POST,PUT,etc. here
}
I created a main function to see what happens if I specify the JAXBContext:
main for testing console output:
public class Main {
public static void main(String[] args) {
StudentService studentDao = new StudentService();
StudentDao.Student stu1 = new StudentDao.Student();
stu1.setName("marc");
stu1.setGrade("A");
StudentDao.Student stu2 = new StudentDao.Student();
stu2.setName("jacob");
stu2.setGrade("B");
StudentDao.Student stu3 = new StudentDao.Student();
stu3.setName("anthony");
stu3.setGrade("A");
studentDao.createStudent(stu1);
studentDao.createStudent(stu2);
studentDao.createStudent(stu3);
try {
File file = new File("out.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(StudentService.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(studentDao, file);
jaxbMarshaller.marshal(studentDao, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
}
When I run this main function to see the output I actually get this, which does NOT match the REST output of course:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student-list>
<studentDao>instance</studentDao>
</student-list>
Which seems promising, but I cannot get seem to get the top level class name to be overridden in the REST calls, which is ultimately what I need.
Any other ideas of what I'm doing wrong here ?
Upvotes: 4
Views: 771
Reputation: 351
Students class
import java.util.ArrayList;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "student-list")
@XmlAccessorType(XmlAccessType.FIELD)
public class Students{
//students
@XmlElement(name = "student")
public ArrayList<Student> studentsLst;
//getters and setters
public ArrayList<Student> getStudentsLst() {
return studentsLst;
}
public void setStudentsLst(ArrayList<Student> studentsLst) {
this.studentsLst = studentsLst;
}
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Student {
private String name;
private String grade;
public Student() {
}
public Student(String name, String grade) {
super();
this.name = name;
this.grade = grade;
}
// getters/setters ...
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
}
}
main method /marshaller use:
public static void main(String[] args) {
Students.Student stu1 = new Students.Student();
stu1.setName("marc");
stu1.setGrade("A");
Students.Student stu2 = new Students.Student();
stu2.setName("jacob");
stu2.setGrade("B");
Students.Student stu3 = new Students.Student();
stu3.setName("anthony");
stu3.setGrade("A");
ArrayList<Students.Student> studentsLst = new ArrayList<>();
studentsLst.add(stu1);
studentsLst.add(stu2);
studentsLst.add(stu3);
Students students = new Students();
students.setStudentsLst(studentsLst);
try {
File file = new File("C:\\file.xml");
JAXBContext jaxbContext = JAXBContext.newInstance(Students.class);
Marshaller jaxbMarshaller = jaxbContext.createMarshaller();
// output pretty printed
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
jaxbMarshaller.marshal(students, file);
jaxbMarshaller.marshal(students, System.out);
} catch (JAXBException e) {
e.printStackTrace();
}
}
output generated:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<student-list>
<student>
<name>marc</name>
<grade>A</grade>
</student>
<student>
<name>jacob</name>
<grade>B</grade>
</student>
<student>
<name>anthony</name>
<grade>A</grade>
</student>
</student-list>
PS: Since you are using REST, the main method given above can be used for your FYI. Use this main method to build your studentService.getStudentAsList()
method accordingly. (My assumption is that it shall pretty much give the same xml.) :)
Upvotes: 1