You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot中多模型JSON转自定义XML时JAXB IllegalAnnotationsException问题排查

解决JAXB编组IllegalAnnotationsException问题及JSON转XML实现方案

看起来你的问题核心出在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; }
    }
}

四、关键注意事项

  1. 业务规则补充:原JSON里没有Subject、Gender、RollNumber的对应关系,你需要根据实际业务定义映射规则(比如从数据库读取、配置文件配置),上面的示例只是模拟。
  2. JSON反序列化:实际项目中用Jackson的ObjectMapper.readValue(jsonString, new TypeReference<List<StudentDetails>>() {})即可完成JSON到对象列表的转换。
  3. JAXB注解细节:用@XmlValue可以准确映射XML元素的文本内容,生成的结构会完全匹配你的目标XML。

内容的提问来源于stack exchange,提问作者Bhanu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.30 11:27:38