如何解决带有 XmlAdapter 的 JAXB @XmlAnyElement 不会调用解组方法
我正在尝试解组 XML
并将其映射到 Java POJO。我的 XML 可以有一些用户定义的元素,这些元素可以是随机的,所以我想存储它们。经过研究,我发现我可以使用 @XmlAnyElement(lax=true)
。我正在尝试将 XMLAdapter
与 @XmlAnyElement
一起使用,但由于某种原因,我的 unmarshal
中的方法 XMLAdapter
根本没有被调用,因为我无法映射 user-defined
字段。
有人可以向我解释如何将 unmarshal
字段user-defined
Map<String,Object>
的 marshalling
一切正常,我正在使用方法 {{3 }}。但是 unmarshalling
根本没有被调用,这让我有点困惑。
以下是我的 XML
,它需要是 unmarshalled
。 (请注意,namespace
可以是动态的和用户定义的,这可能会在每个 xml 中发生变化):
<Customer xmlns:google="https://google.com">
<name>Batman</name>
<google:main>
<google:sub>bye</google:sub>
</google:main>
</Customer>
以下是我的 Customer
类,XML
需要 unmarshalled
;
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer",propOrder = {"name","others"})
@XmlAccessorType(XmlAccesstype.FIELD)
public class Customer {
private String name;
@XmlAnyElement(lax = true)
@XmlJavaTypeAdapter(TestAdapter.class)
private Map<String,Object> others = new HashMap<>();
//Getter Setter and other constructors
}
以下是我的 XMLAdapter (TestAdapter)
类,它将用于 marshalling
和 unmarshalling
user-defined
字段。根本没有调用 unmarshalling
方法。但是,marshalling
方法基于 mentioned here 按预期工作。
class TestAdapter extends XmlAdapter<Wrapper,Map<String,Object>> {
@Override
public Map<String,Object> unmarshal(Wrapper value) throws Exception {
//Method not being called at all the following SYstem.OUT is NOT PRINTED
System.out.println("INSIDE UNMARSHALLING METHOD TEST");
System.out.println(value.getElements());
return null;
}
@Override
public Wrapper marshal(Map<String,Object> v) throws Exception {
return null;
}
}
class Wrapper {
@XmlAnyElement
List elements;
}
我使用了 package-info.java
文件,其中填充了以下内容:
@jakarta.xml.bind.annotation.XmlSchema(namespace = "http://google.com",elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED)
package io.model.jaxb;
我研究了很多,但找不到任何类似的答案。此外,尝试了很多东西,但没有奏效。因此,在这里发布相同的内容并寻找一些建议或解决方法。
我对 unmarshalling
几乎没有怀疑:
- 为什么在解组过程中没有调用我的
XMLAdapter TestAdapter.class
unmarshal
方法? - 如何
unmarshal
可以随机出现的 XML 字段和命名空间? - 是我做错了什么还是我应该做些什么来读取动态显示的命名空间和元素?
*** FOLLOWING IS EDITED SECTION BASED ON RESPONSE FROM Thomas Fritsch ****
根据回复,我编辑了我的课程,但仍然没有按预期工作:
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer","others"})
@XmlAccessorType(XmlAccesstype.FIELD)
public class Customer {
private String name;
@JsonIgnore
@XmlJavaTypeAdapter(TestAdapter.class)
private List<Object> others;
@XmlTransient
@XmlAnyElement(lax = true)
private List<Element> another = new ArrayList<>();
}
所以发生的事情是,如果我使用 @XmlTransient
那么字段 another
将不会在 unmarshalling
期间填充,我需要保留它 @XmlTransient
因为我没有在 marshalling
期间想要它同样我已经为 @JsonIgnore
制作了 Map<String,Object> others
因为我在 unmarshalling
期间不需要它但是两者相互冲突并且无法获得所需的输出。
我的主要目标是将 XML
文件转换为 JSON
,反之亦然。对于上面提到的 XML
文件,我想在 JSON
中获得以下输出:
{
"Customer": {
"name": "BATMAN","google:main": {
"google:sub": "bye"
}
}
}
同样,当我转换这个 JSON
时,我应该得到原始的 XML
。
以下是我的Main.class
:
class Main {
public static void main(String[] args) throws JAXBException,XMLStreamException,JsonProcessingException {
//XML to JSON
JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStream inputStream = Main.class.getClassLoader().getResourceAsstream("Customer.xml");
final XMLInputFactory xmlInputFactory = XMLInputFactory.newInstance();
final XMLStreamReader streamReader = xmlInputFactory.createXMLStreamReader(inputStream);
final Customer customer = unmarshaller.unmarshal(streamReader,Customer.class).getValue();
final ObjectMapper objectMapper = new ObjectMapper();
final String jsonEvent = objectMapper.writerWithDefaultPrettyPrinter().writeValueAsstring(customer);
System.out.println(jsonEvent);
//JSON to XML
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FRAGMENT,Boolean.TRUE);
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,Boolean.TRUE);
marshaller.marshal(customer,System.out);
}
}
解决方法
您的 Map
属性的 others
类型不适合 @XmlAnyElement
。
根据其 javadoc @XmlAnyElement
annotation
旨在与 List
或数组属性一起使用
(通常带有 List
或 org.w3c.dom.Element
数组)。
可能您将其与 @XmlAnyAttribute
annotation
这确实与 Map
属性一起使用。
因此,在您的 Customer
类中,不使用适配器的 others
属性将如下所示:
姓名;
@XmlAnyElement(lax = true)
private List<Element> others = new ArrayList<>();
使用适配器的 others
属性应如下所示:
@XmlAnyElement(lax = true)
@XmlJavaTypeAdapter(TestAdapter.class)
private List<MyObject> others = new ArrayList<>();
这样做时,JAXB 将实际调用您的适配器。
适配器的工作是在 Element
和 MyObject
之间转换。
public class TestAdapter extends XmlAdapter<Element,MyObject> {
@Override
public MyObject unmarshal(Element v) throws Exception {
...
}
@Override
public Element marshal(MyObject v) throws Exception {
...
}
}
,
尝试了很多东西后,我能够让它发挥作用。发布相同的解决方案,以便将来对某人有所帮助。
我使用了 Project Lombok
,因此您可以看到一些额外的注释,例如 @Getter,@Setter,etc
Method-1:
正如@Thomas Fritsch 提到的,你必须将 @XmlAnyElement(lax=true)
与 List<Element>
一起使用,我在使用 Map<String,Object>
。
Method-2:
您可以继续使用 Map<String,Object>
并将 @XmlPath(".")
与适配器配合使用以使其正常工作。在此处发布相同的代码:(我添加了一个额外的字段 age
other,所有内容都保持不变)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer",propOrder = {"name","age","others"})
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Customer {
@XmlElement(name = "name")
private String name;
private String age;
@XmlJavaTypeAdapter(TestAdapter.class)
@XmlPath(".")
private Map<String,Object> others;
}
以下是 TestAdapter.class
发布的相同内容以供参考。当您 unmarhsal
时,unmarshal
中的方法 TestAdapter
将被调用,您可以执行任何您需要的操作。
class TestAdapter extends XmlAdapter<Wrapper,Map<String,Object>> {
@Override
public Map<String,Object> unmarshal(Wrapper value) throws Exception {
System.out.println("INSIDE UNMARSHALLING METHOD TEST");
final Map<String,Object> others = new HashMap<>();
for (Object obj : value.getElements()) {
final Element element = (Element) obj;
final NodeList children = element.getChildNodes();
//Check if its direct String value field or complex
if (children.getLength() == 1) {
others.put(element.getNodeName(),element.getTextContent());
} else {
List<Object> child = new ArrayList<>();
for (int i = 0; i < children.getLength(); i++) {
final Node n = children.item(i);
if (n.getNodeType() == Node.ELEMENT_NODE) {
Wrapper wrapper = new Wrapper();
List childElements = new ArrayList();
childElements.add(n);
wrapper.elements = childElements;
child.add(unmarshal(wrapper));
}
}
others.put(element.getNodeName(),child);
}
}
return others;
}
@Override
public Wrapper marshal(Map<String,Object> v) throws Exception {
Wrapper wrapper = new Wrapper();
List elements = new ArrayList();
for (Map.Entry<String,Object> property : v.entrySet()) {
if (property.getValue() instanceof Map) {
elements.add(new JAXBElement<Wrapper>(new QName(property.getKey()),Wrapper.class,marshal((Map) property.getValue())));
} else {
elements.add(new JAXBElement<String>(new QName(property.getKey()),String.class,property.getValue().toString()));
}
}
wrapper.elements = elements;
return wrapper;
}
}
@Getter
class Wrapper {
@XmlAnyElement
List elements;
}
虽然它适用于特定情况,但我发现使用这种方法存在一个问题。 I have created a new post for this issue. 如果我收到有关该问题的任何回复,我将尝试更新代码,使其能够正常工作。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。