【发布时间】:2021-08-11 09:26:47
【问题描述】:
简而言之,我想执行unmarshalling as mentioned here 但与Map 一起我将再执行一个@XmlElement。所以一个字段用(Map field) @XmlPath(".")注释,另一个字段用(String field) @XmlElement注释,然后我想执行unmarshalling。
我的应用程序的主要目标是使用 JAXB/Moxy and Jackson 库转换 XML->JSON 和 JSON->XML。我正在尝试unmarshal XML 并将其映射到 Java POJO。我的 XML 可以有一些专用元素和一些用户定义的元素,这些元素可以随机出现,所以我想将它们存储在 Map<String, Object> 中。因此,我正在使用XMLAdapter。我正在关注blog article to do so。我做的不完全一样,但有点不同。
我面临的问题是在unmarshalling 期间根本没有考虑专用字段。所有值都是unmarshalled 到Map<String.Object>。据我了解,这是由于注释@XmlPath(".") 和XMLAdapter 的使用而发生的,但是如果我删除此注释,则它将无法按预期工作。有人可以帮我解决这个问题吗? marshaling 与 @XmlPath(".") 和 XMLAdapter 都可以正常工作。该问题仅在unmarshalling 期间出现。
以下是我想转换为JSON 的XML:(注意:Name 和Age 是专用字段,others 是user-defined 字段。)
<Customer xmlns:google="https://google.com">
<name>BATMAN</name>
<age>2008</age>
<google:main>
<google:sub>bye</google:sub>
</google:main>
</Customer>
以下是我的Customer 类,用于marshaling、unmarshalling,由Moxy 和Jackson:(注意:Name 和Age 是专用字段,others 是user-defined字段。我希望 others 仅存储无法直接映射到 POJO 的值,例如 google:main 及其来自上述 XML 的子项)
@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "others"})
@XmlAccessorType(XmlAccessType.FIELD)
public class Customer {
private String name;
private String age;
@XmlPath(".")
@XmlJavaTypeAdapter(TestAdapter.class)
private Map<String, Object> others;
//Getter, Setter and other constructors
}
以下是我的TestAdapter 类,将用于Userdefined 字段:
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;
}
最后,我的Main 类将用于marshaling 和unmarshalling。另外,要转换为 JSON 和 XML。
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);
}
}
当我转换 XML->JSON 时,我得到以下输出:(如果您观察到字段 name 和 age 不被视为 Customer 类的专用字段,而是将其视为随机字段并写入在others)
{
"name" : "",
"age" : "",
"others" : {
"google:main" : [ {
"google:sub" : "bye"
} ],
"name" : "BATMAN",
"age" : "2008"
}
}
我希望我的输出是这样的:(我希望先映射我的专用字段,然后如果有任何未知字段,然后在 others MAP 中映射它们)。请注意,我不想在我的JSON 中获得others 标签。我只想获取专用字段的字段名称。
{
"name": "BATMAN",
"age": 2008,
"google:main": {
"google:sub": "bye"
}
}
以下是我想在marshaling 期间获得的XML。另外,请注意我使用的是@XmlPath("."),因此在marshaling 期间我不会在XML 中获得others 节点。
<Customer>
<name>BATMAN</name>
<age>2008</age>
<google:main>>
<google:sub>bye</google:sub>
</google:main>
</Customer>
marshaling 工作正常。问题发生在 .unmarshaling 期间,据我了解,这是由于注释 @XmlPath(".") 和 XMLAdapter 而发生的,但如果我删除此注释,则它将无法按预期工作。有人可以帮我解决这个问题吗?
** 已编辑 **
我想了一些解决方法,但似乎没有什么对我有用。由于@XmlPath("."),他们搞砸了。仍在寻找一些想法或解决方法。任何帮助将不胜感激。
【问题讨论】:
-
即使在尝试了很多事情和调试之后也无法找到解决方案或解决方法。如果有人有一些解决方案,请提出一些建议。
-
@Rise_Against 提供的答案对我不起作用。仍在寻找一些建议或解决方法。请帮忙。
-
代替
XmlPath("."),试试Customer/*[not(self::name | self::age)]或*[not(self::name | self::age)] -
@LuisMuñoz 非常感谢您花时间回复。你的意思是用
@XmlPath(""Customer/*[not(self::name | self::age)])替换@XmlPath(".")?还是用Customer/*[not(self::name | self::age)]替换整个@XmlPath(".")?我有点困惑,您能否提供一个示例或指导我阅读一些涵盖这些内容的文档。提前致谢。 -
自己试过了,还是不行。
标签: java xml jaxb unmarshalling moxy