如何使用 JAXB2 将 XML 与动态元素编组
How to marshall XML with dynamic element using JAXB2
我正在开发一个 SOAP 客户端,我正在寻找更复杂的解决方案来使用 Jaxb2 库将对象编组为 XML-string。
目标是整理一个对象,该对象充当任何类型元素的包装器。示例:
<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 根(操作)包含“员工”或“部门”或其他任何内容。
我目前的工作方案如下:
@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;
}
这很好用,但我正在寻找更通用的解决方案,而不需要为每种类型创建 class(*Action extends Action)。元素的名称必须始终与(动态)类型的 class 名称相同。我的想法是这样的:
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();
}
这样的事情可能吗?
提前致谢。
对于上面提供的XML:
你可以这样做
方法一
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;
}
Employee.class;
@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Employee extends ActionItem {
@XmlAttribute(name = "id")
private String id;
}
Department.class;
@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Department extends ActionItem {
@XmlAttribute
private String id;
@XmlElement(name = "ParentId")
private String parentID;
}
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);
}
}
如果您提供 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>
方法二
创建界面并使用它:
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;
}
Department.class:
@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Department {
@XmlAttribute
private String id;
@XmlElement(name = "ParentId")
private String parentID;
@XmlElement(name = "Name")
protected String name;
}
这将提供相同的输出。
我正在开发一个 SOAP 客户端,我正在寻找更复杂的解决方案来使用 Jaxb2 库将对象编组为 XML-string。
目标是整理一个对象,该对象充当任何类型元素的包装器。示例:
<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 根(操作)包含“员工”或“部门”或其他任何内容。
我目前的工作方案如下:
@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;
}
这很好用,但我正在寻找更通用的解决方案,而不需要为每种类型创建 class(*Action extends Action)。元素的名称必须始终与(动态)类型的 class 名称相同。我的想法是这样的:
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();
}
这样的事情可能吗?
提前致谢。
对于上面提供的XML:
你可以这样做方法一
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;
}
Employee.class;
@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Employee extends ActionItem {
@XmlAttribute(name = "id")
private String id;
}
Department.class;
@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Department extends ActionItem {
@XmlAttribute
private String id;
@XmlElement(name = "ParentId")
private String parentID;
}
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);
}
}
如果您提供 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>
方法二
创建界面并使用它:
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;
}
Department.class:
@Data
@XmlAccessorType(XmlAccessType.NONE)
public class Department {
@XmlAttribute
private String id;
@XmlElement(name = "ParentId")
private String parentID;
@XmlElement(name = "Name")
protected String name;
}
这将提供相同的输出。