微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

带有 XmlAdapter 的 JAXB @XmlAnyElement 不会调用解组方法

如何解决带有 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) 类,它将用于 marshallingunmarshalling 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 几乎没有怀疑:

  1. 为什么在解组过程中没有调用我的 XMLAdapter TestAdapter.class unmarshal 方法
  2. 如何unmarshal可以随机出现的 XML 字段和命名空间?
  3. 是我做错了什么还是我应该做些什么来读取动态显示的命名空间和元素?
*** 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 或数组属性一起使用 (通常带有 Listorg.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 将实际调用您的适配器。 适配器的工作是在 ElementMyObject 之间转换。

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 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?