Spring Boot中多模型JSON转自定义XML时JAXB IllegalAnnotationsException问题排查
看起来你的问题核心出在POJO类不符合JAXB序列化规范,以及模型结构和目标XML不匹配上,咱们一步步拆解解决:
一、先搞定JAXB报错的核心原因
IllegalAnnotationsException几乎都是因为你的POJO违反了JAXB的基本要求,先修正这些基础问题:
1. 所有JAXB序列化类必须有无参构造器
JAXB在实例化对象时需要调用无参构造,你的Subject、Student类只有带参构造,这是第一个坑。
2. Getter/Setter方法必须严格遵循JavaBean规范
比如你的Section类里的getsubject()应该是getSubject(),setsubject()改为setSubject();Subject类里的getstudentSubjectMap()要改成getStudentSubjectMap(),否则JAXB无法识别属性。
3. 集合类型要对应目标XML的结构
你的Section类里定义的是单个Subject,但目标XML里每个Section下有多个Subject,所以应该用List<Subject>而不是单个对象;同理Subject类里应该是List<Student>而不是单个Student。
4. Map类型无法被JAXB直接序列化
目标XML里没有Map的结构,所以StudentSubjectMap不应该作为POJO的序列化属性,它只是你分组时的临时变量,应该从Subject类里移除。
二、修正后的POJO模型
以下是符合JAXB规范且匹配目标XML结构的模型类:
Class.java
import javax.xml.bind.annotation.XmlRootElement; import javax.xml.bind.annotation.XmlElement; import java.util.List; @XmlRootElement(name = "Class") public class Class { private List<Section> sections; private String principalName; // 必须的无参构造器 public Class() {} @XmlElement(name = "Section") public List<Section> getSections() { return sections; } public void setSections(List<Section> sections) { this.sections = sections; } @XmlElement(name = "PrincipalName") public String getPrincipalName() { return principalName; } public void setPrincipalName(String principalName) { this.principalName = principalName; } }
Section.java
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlValue; import java.util.List; public class Section { private String name; private List<Subject> subjects; public Section() {} // 用@XmlValue映射Section的文本内容(比如<Section>A) @XmlValue public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name = "Subject") public List<Subject> getSubjects() { return subjects; } public void setSubjects(List<Subject> subjects) { this.subjects = subjects; } }
Subject.java
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlValue; import java.util.List; public class Subject { private String name; private List<Student> students; public Subject() {} // 用@XmlValue映射Subject的文本内容(比如<Subject>Maths) @XmlValue public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name = "Student") public List<Student> getStudents() { return students; } public void setStudents(List<Student> students) { this.students = students; } }
Student.java
import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlValue; public class Student { private String name; private String gender; private int rollNumber; public Student() {} // 用@XmlValue映射Student的文本内容(比如<Student>Brendon) @XmlValue public String getName() { return name; } public void setName(String name) { this.name = name; } @XmlElement(name = "Gender") public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } @XmlElement(name = "RollNumber") public int getRollNumber() { return rollNumber; } public void setRollNumber(int rollNumber) { this.rollNumber = rollNumber; } }
三、JSON转POJO再生成XML的正确流程
1. 先把JSON转成StudentDetails列表
假设你用Jackson或者Gson把接收的JSON转成List<StudentDetails>(实际项目中用ObjectMapper即可实现)。
2. 按目标XML结构分组转换
你的原JSON里没有Subject、Gender、RollNumber的对应关系,所以需要补充业务规则,以下是示例实现:
import javax.xml.bind.JAXBContext; import javax.xml.bind.JAXBException; import javax.xml.bind.Marshaller; import java.io.File; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.stream.Collectors; public class Main { public static void main(String[] args) { // 步骤1:模拟从服务接收的JSON转成StudentDetails列表 List<StudentDetails> studentDetailsList = getMockStudentDetails(); // 步骤2:按Section分组 Map<String, List<StudentDetails>> sectionGroup = studentDetailsList.stream() .collect(Collectors.groupingBy(StudentDetails::getSection)); // 步骤3:构建Class对象 Class classObj = new Class(); classObj.setPrincipalName(studentDetailsList.get(0).getPrincipal()); // 假设所有学生的Principal相同 List<Section> sections = new ArrayList<>(); // 处理每个Section sectionGroup.forEach((sectionName, studentsInSection) -> { Section section = new Section(); section.setName(sectionName); List<Subject> subjects = new ArrayList<>(); // 根据业务规则给Section分配Subject if ("A".equals(sectionName)) { // 构建Maths Subject Subject mathsSubject = new Subject(); mathsSubject.setName("Maths"); List<Student> mathsStudents = studentsInSection.stream() .filter(s -> "Brendon".equals(s.getName()) || "Monica".equals(s.getName())) .map(Main::convertToStudent) .collect(Collectors.toList()); mathsSubject.setStudents(mathsStudents); subjects.add(mathsSubject); // 构建English Subject Subject englishSubject = new Subject(); englishSubject.setName("English"); List<Student> englishStudents = studentsInSection.stream() .filter(s -> "Brendon".equals(s.getName()) || "Gina".equals(s.getName())) .map(Main::convertToStudent) .collect(Collectors.toList()); englishSubject.setStudents(englishStudents); subjects.add(englishSubject); } else if ("B".equals(sectionName)) { // 构建Science Subject Subject scienceSubject = new Subject(); scienceSubject.setName("Science"); List<Student> scienceStudents = studentsInSection.stream() .filter(s -> "Justin".equals(s.getName()) || "Sony".equals(s.getName())) .map(Main::convertToStudent) .collect(Collectors.toList()); scienceSubject.setStudents(scienceStudents); subjects.add(scienceSubject); // 构建English Subject Subject englishSubject = new Subject(); englishSubject.setName("English"); List<Student> englishStudents = studentsInSection.stream() .filter(s -> "Stephen".equals(s.getName()) || "Lucky".equals(s.getName())) .map(Main::convertToStudent) .collect(Collectors.toList()); englishSubject.setStudents(englishStudents); subjects.add(englishSubject); } section.setSubjects(subjects); sections.add(section); }); classObj.setSections(sections); // 步骤4:JAXB编组生成XML try { JAXBContext jc = JAXBContext.newInstance(Class.class); Marshaller m = jc.createMarshaller(); m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); // 输出到文件 m.marshal(classObj, new File("class.xml")); // 输出到控制台 m.marshal(classObj, System.out); } catch (JAXBException e) { e.printStackTrace(); } } // 辅助方法:把StudentDetails转成Student对象(补充Gender和RollNumber的映射规则) private static Student convertToStudent(StudentDetails details) { Student student = new Student(); student.setName(details.getName()); // 示例:根据Name推断Gender student.setGender("Brendon".equals(details.getName()) || "Stephen".equals(details.getName()) || "Justin".equals(details.getName()) ? "M" : "F"); // 示例:根据目标XML的RollNumber设置规则 switch (details.getName()) { case "Brendon": student.setRollNumber(1); break; case "Monica": student.setRollNumber(2); break; case "Gina": student.setRollNumber(3); break; case "Justin": student.setRollNumber(4); break; case "Stephen": student.setRollNumber(5); break; case "Lucky": student.setRollNumber(3); break; case "Sony": student.setRollNumber(2); break; default: student.setRollNumber(0); } return student; } // 模拟从服务接收的StudentDetails列表 private static List<StudentDetails> getMockStudentDetails() { List<StudentDetails> list = new ArrayList<>(); list.add(new StudentDetails("Lucky", "B", "", 4, "MCCDDVP1", "Mary")); list.add(new StudentDetails("Brendon", "A", "", 1, "MCCDDVP1", "Mary")); list.add(new StudentDetails("Gina", "A", "", 2, "MCCDDVP1", "Mary")); list.add(new StudentDetails("Monica", "A", "", 3, "MCCDDVP1", "Mary")); list.add(new StudentDetails("Stephen", "B", "", 5, "MCCDDVP1", "Mary")); list.add(new StudentDetails("Justin", "B", "", 6, "MCCDDVP1", "Mary")); list.add(new StudentDetails("Sony", "B", "", 7, "MCCDDVP1", "Mary")); return list; } // 模拟你的StudentDetails类 static class StudentDetails { private String Name; private String Section; private String Subjects; private int StudentId; private String Hobies; private String Principal; public StudentDetails(String name, String section, String subjects, int studentId, String hobies, String principal) { Name = name; Section = section; Subjects = subjects; StudentId = studentId; Hobies = hobies; Principal = principal; } // Getters public String getName() { return Name; } public String getSection() { return Section; } public String getSubjects() { return Subjects; } public int getStudentId() { return StudentId; } public String getHobies() { return Hobies; } public String getPrincipal() { return Principal; } } }
四、关键注意事项
- 业务规则补充:原JSON里没有Subject、Gender、RollNumber的对应关系,你需要根据实际业务定义映射规则(比如从数据库读取、配置文件配置),上面的示例只是模拟。
- JSON反序列化:实际项目中用Jackson的
ObjectMapper.readValue(jsonString, new TypeReference<List<StudentDetails>>() {})即可完成JSON到对象列表的转换。 - JAXB注解细节:用
@XmlValue可以准确映射XML元素的文本内容,生成的结构会完全匹配你的目标XML。
内容的提问来源于stack exchange,提问作者Bhanu

