[英]JAXB @XmlAnyElement with XmlAdapter does not make a call to the unmarshal method
我正在尝试将XML
和 map 解组为 Java POJO。 我的 XML 可以有一些用户定义的元素,这些元素可以是随机的,所以我想存储它们。 经过研究,我发现我可以使用@XmlAnyElement(lax=true)
。 我正在尝试将XMLAdapter
与@XmlAnyElement
一起使用,但由于某种原因,我的XMLAdapter
中的unmarshal
组方法根本没有被调用,因此我无法 map user-defined
字段。
有人可以向我解释如何将user-defined
字段unmarshal
组到我的Map<String,Object>
以便marshalling
一切正常,我正在使用这里提到的方法。 但是根本没有调用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
组 XML ;
@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)
unmarshalling
,它将用于marshalling
和解组user-defined
字段。 根本没有调用unmarshalling
方法。 然而,基于此处提供的代码, marshalling
方法按预期工作。
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 ****
根据回复,我编辑了我的 class 但仍无法按预期工作:
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "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
,那么在unmarshalling
组期间将不会填充another
字段,我需要保留它@XmlTransient
,因为我在marshalling
@JsonIgnore
中不想要它,类似地我为Map<String, Object> others
,因为我在unmarshalling
组过程中不需要它,但两者相互冲突,无法获得所需的 output。
我的主要目标是将XML
文件转换为JSON
,反之亦然。 对于上面提到的XML
文件,我想在 JSON 中获得以下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);
}
}
您others
属性的Map
类型不适合@XmlAnyElement
。
根据其 javadoc, @XmlAnyElement
注释旨在与List
或数组属性一起使用(通常与List
或org.w3c.dom.Element
数组一起使用)。 可能您已经将此与@XmlAnyAttribute
注释混淆了,后者确实与Map
属性一起使用。
因此,在您的Customer
class 中,不使用适配器的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
,其他一切都保持不变)
@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;
}
尽管它适用于特定情况,但使用这种方法我发现了一个问题。 我为此问题创建了一个新帖子。 如果我对此问题有任何回应,那么我将尝试更新代码,使其能够正常工作。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.