如何使用 JAXB2 用动态元素编组 XML

Posted

技术标签:

【中文标题】如何使用 JAXB2 用动态元素编组 XML【英文标题】:How to marshall XML with dynamic element using JAXB2 【发布时间】:2021-09-24 05:48:31 【问题描述】:

我正在开发一个 SOAP 客户端,我正在寻找更复杂的解决方案来使用 Jaxb2 库将对象编组为 XML 字符串。

目标是编组一个对象,该对象充当任何类型元素的包装器。示例:

<Action id="5">
   <Employee id="10">
      <Name>John</Name>
   </Employee>
</Action>

或者。

 <Action id="5">
    <Department id="ABC">
       <Name>Economy Department</Name>
       <ParentId>CDE</ParentId>
    </Department>
 </Action>

注意:xml 根(操作)包含“Employee”或“Department”或其他任何内容。

我目前的工作解决方案如下:

@XmlRootElement(name = "Action")
abstract class Action 

   @XmlAttribute(name = "id")
   protected String id;



class EmployeeAction extends Action 
    
   @XmlElement(name = "Employee")
   protected Employee employee;



class DepartmentAction extends Action 
    
   @XmlElement(name = "Department")
   protected Department department;


这很好用,但我正在寻找更通用的解决方案,而不需要为每种类型创建类(*Action extends Action)。元素的名称必须始终与(动态)类型的 className 相同。我的想法是这样的:

public class Action<T> 

   @XmlAttribute(name = "id")
   protected String id;

   @XmlElement(name = "getClass().getSimpleName()") //???
   protected T element;


... 并编组如下内容:

Action<?> action = ...;

JAXBContext context = JAXBContext.newInstance(Action.class, action.getElement().getClass());
Marshaller marshaller = context.createMarshaller();

try(ByteArrayOutputStream outStream = new ByteArrayOutputStream()) 
   marshaller.marshal(action, outStream);
   return outStream.toString();

这样的事情可能吗?

提前致谢。

【问题讨论】:

【参考方案1】:

您可以对上面提供的 XML 执行类似的操作:

方法-1

Action.class;

@XmlRootElement(name = "Action")
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Action 
    @XmlAttribute(name = "id")
    protected String id;

    @XmlElements(
            @XmlElement(name = "Employee", type = Employee.class),
            @XmlElement(name = "Department", type = Department .class),
    )
    private ActionItem type;

ActionItem.class;

@Data
@XmlAccessorType(XmlAccessType.NONE)
public class ActionItem 
    @XmlElement(name = "Name")
    protected String name;

雇员.class;

@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Employee extends ActionItem 

    @XmlAttribute(name = "id")
    private String id;

部门.class;

@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Department extends ActionItem 
    @XmlAttribute
    private String id;

    @XmlElement(name = "ParentId")
    private String parentID;

主类:


public class Main 
    public static void main(String[] args) throws JAXBException, XMLStreamException 
        final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("action.xml");
        final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
        final Unmarshaller unmarshaller = JAXBContext.newInstance(Action.class).createUnmarshaller();
        final Action action = unmarshaller.unmarshal(xmlStreamReader, Action.class).getValue();
        System.out.println(action.toString());

        Marshaller marshaller = JAXBContext.newInstance(Action.class).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(action, System.out);
    

如果您提供了Employee XML,那么它会产生以下结果:

Action(id=5, type=Employee(id=10))
<Action id="5">
   <Employee id="10">
      <Name>John</Name>
   </Employee>
</Action>

如果您提供Department XML,那么它会产生以下结果:

Action(id=5, type=Department(parentID=CDE))
<Action id="5">
   <Department>
      <Name>Economy Department</Name>
      <ParentId>CDE</ParentId>
   </Department>
</Action>

方法-2

创建界面并使用它:

public interface ActionItem2 

Action.class 使用创建的接口。

@XmlRootElement(name = "Action")
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Action 
    @XmlAttribute(name = "id")
    protected String id;

    @XmlElements(
            @XmlElement(name = "Employee", type = Employee.class),
            @XmlElement(name = "Department", type = Department .class),
    )
    private ActionItem2 type;

Employee.class 实现了创建的接口

@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Employee implements ActionItem2 

    @XmlAttribute(name = "id")
    private String id;

    @XmlElement(name = "Name")
    protected String name;

实现所创建接口的Department.class

@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Department implements ActionItem2 
    @XmlAttribute
    private String id;

    @XmlElement(name = "ParentId")
    private String parentID;

    @XmlElement(name = "Name")
    protected String name;

Main.class(无变化)

public class Main 
    public static void main(String[] args) throws JAXBException, XMLStreamException 
        final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("action.xml");
        final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
        final Unmarshaller unmarshaller = JAXBContext.newInstance(Action.class).createUnmarshaller();
        final Action action = unmarshaller.unmarshal(xmlStreamReader, Action.class).getValue();
        System.out.println(action.toString());

        Marshaller marshaller = JAXBContext.newInstance(Action.class).createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
        marshaller.marshal(action, System.out);
    

结果是一样的。

方法 - 3

如果您不想修改 POJO,则可以执行以下操作:

@XmlRootElement(name = "Action")
@Data
@XmlAccessorType(XmlAccessType.FIELD)
public class Action 
    @XmlAttribute(name = "id")
    protected String id;

    @XmlElements(
            @XmlElement(name = "Employee", type = Employee.class),
            @XmlElement(name = "Department", type = Department .class),
    )
    private Object type;


Employee.class:

@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Employee 

    @XmlAttribute(name = "id")
    private String id;

    @XmlElement(name = "Name")
    protected String name;

部门.类:


@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Department 

    @XmlAttribute
    private String id;

    @XmlElement(name = "ParentId")
    private String parentID;

    @XmlElement(name = "Name")
    protected String name;

这将提供相同的输出。

【讨论】:

感谢您的回答。可接受的解决方案,但是否有任何其他解决方案不需要修改我的 Employee 和 Department 类(使用扩展)?我的实体类是从每次构建的 XSD 模式生成的。谢谢! @CSB 用Method-3 修改了答案。看看这个。如果有帮助,请点赞。

以上是关于如何使用 JAXB2 用动态元素编组 XML的主要内容,如果未能解决你的问题,请参考以下文章

如何在一个Jaxb2Marshaller中处理更多的WSDL?

在java中编组时如何更改xml元素名称

如何(un)编组子类中根元素的值

如何在IJ中使用Jaxb2通过xml定义生成对应的Java Entity类的文件

如何克隆JAXB对象

如何使用 JAXB2.0 禁用 DTD 获取