2016-05-25 21 views
3

UPDATE: Letztendlich versuchte ich alles hier vorgeschlagen und schien einige Fortschritte zu haben, aber meine API wurde für den beabsichtigten Zweck überentwickelt und ich lief die Zeit ab, so wechselte ich zu hart Codierung "Student -list "in der String-Antwort meines GET/Student Calls, die für den Proof of Concept mehr als genug war.JAXB Liste als oberste Ebene XML

Ich arbeite an einem einfachen REST-Service, der XML verwendet, um mit Studentenakten zu arbeiten.

, wenn ich ein GET auf/Student bekomme ich:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<students> 
    <student> 
     <grade>B</grade> 
     <name>Jane</name> 
    </student> 
    ... 
</students> 

Allerdings würde ich die Ausgabe gerne sein:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<student-list> 
    <student> 
     <grade>B</grade> 
     <name>Jane</name> 
    </student> 
    ... 
</student-list> 

Hier ist mein Student.java Schnipsel:

@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 ... 

} 

Ich habe versucht, das XmlRootElement zu ändern "Student-lis t ", aber das macht jedes Unterelement der Liste zu einem Schülerlistentyp und ignoriert die Elementliste der obersten Ebene.

Das Projekt verwendet JAX-RS mit einer StudentSource-Klasse, die ich versucht habe, den XmlRootElement-Namen für diese Ressourcenklasse festzulegen, aber keine Auswirkungen auf die Ausgabe.

Es scheint automatisch die Top-Level-Liste basierend auf der StudentSource-Klasse zu benennen, und ich kann nicht herausfinden, wie diesen Namen zu überschreiben.

Ich habe die Dokumentation zu diesem Thema durchgekämmt, aber Java ist nicht meine stärkste Sprache in diesen Tagen, so habe ich Probleme zu verstehen, wie die Teile für JAXB und JAX-RS zusammenpassen.

Irgendwelche Ideen oder ein Schub in die richtige Richtung?

EDIT: hinzugefügt 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(); 
    } 
    ...   
} 

Basierend auf Mateen Momin das Feedback, habe ich jetzt diese Ausgabe haben durch den REST-Service auf GET/Schüler:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<studentDao_Students> 
    <student> 
     <name>Jane</name> 
     <grade>B</grade> 
    </student> 
    ... 
</studentDao_Students> 

Ich habe ein StudentDao, das von einem StudentService umschlossen wird, der schließlich von Ressourcenklassen aufgerufen wird, um die HTTP-Aufrufe zu ermöglichen. Ich habe meine Schüler Klasse in StudentDao bewegt, was mich gemacht erkennen, dass die StudentService ist, was tatsächlich vermarshallten immer, hier aktualisiert Schnipsel:

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 
} 

StudentenResource. 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   
} 

habe ich eine Hauptfunktion, um zu sehen, was passiert, wenn ich die JAXBContext angeben:

Haupt zum Testen Konsolausgabe:

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(); 
     } 
    } 
} 

Wenn ich ausführen, um diese Hauptfunktion um die Ausgabe zu sehen, bekomme ich tatsächlich das, was natürlich NICHT zur REST-Ausgabe passt:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<student-list> 
    <studentDao>instance</studentDao> 
</student-list> 

Das scheint vielversprechend, aber ich kann nicht bekommen, dass der Klassenname der obersten Ebene in den REST-Aufrufen überschrieben wird, was letztendlich das ist, was ich brauche.

Irgendwelche anderen Ideen, was ich hier falsch mache?

+0

können Sie Ihre 'StudentsResource' oder vielleicht nur relevanten Teil zeigen – varren

+0

Ohne irgendwelche benutzerdefinierten Serialisierungskomponenten zu schreiben, wird jede Unterstützung für diese Implementierung spezifisch sein . Ich weiß, dass RESTEasy dafür Unterstützung bietet. Soweit ich mich erinnern kann, habe Jersey diese Fähigkeit nicht. Welche JAX-RS-Implementierung verwenden Sie? –

+0

Haben Sie '@XmlElementWrapper (name =" student-list ")' 'verwendet? – mattymanme

Antwort

1

Students Klasse

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; 
     } 

    } 

} 

Hauptmethode/marshaller Verwendung:

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(); 
       } 
} 

Ausgabe erzeugt:

<?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: Da Sie REST verwenden, kann die oben angegebene Hauptmethode für Ihre FYI verwendet werden. Verwenden Sie diese Hauptmethode, um Ihre studentService.getStudentAsList() Methode entsprechend zu erstellen. (Meine Annahme ist, dass es ziemlich genau die gleiche XML geben soll.) :)

+0

Ich werde dies heute oder morgen eine Aufnahme später geben. Würde diese Lösung die gleichen Ergebnisse liefern, wenn meine Student-Klasse keine Unterklasse ist? Ich denke, ich habe ein paar Ebenen der Abstraktion, mit denen ich kämpfe. Ich habe eine Student-Klasse, die innerhalb des StudentDao behandelt wird, die vom StudentService aufgerufen wird, der von der StudentsResource aufgerufen wird. In diesem Szenario scheint mein StudentDao sehr ähnlich zu dem zu sein, was Ihre Schülerklasse oben tut, aber ich hatte nicht viel Glück, die JAXB Annotationen auf das Dao zu setzen. Ich habe es nicht versucht, wie du es vorgeschlagen hast. Ich werde es ausprobieren und melden! – chrcoe

+0

Im Grunde erstellt die Implementierung eine innere Klasse 'Student'. Sie können diese innere Klasse in Ihrem studentDao verwenden, indem Sie sie als 'Students.Student' bezeichnen. Wann immer Sie ein jaxb-xml wollen, dient diese äußere' Student'-Klasse als Marshall-Objekt. Versuchen Sie und aktualisieren Sie, glücklich zu helfen :) – mattymanme

+0

Ich war in der Lage, eine Änderung zu bekommen ... nicht die richtige, aber ich werde meinen ursprünglichen Beitrag mit dem neuen Ergebnis aktualisieren. Auch hier spielen mehrere Abstraktionsschichten eine Rolle. – chrcoe