带有 XmlAdapter 的 JAXB @XmlAnyElement 不会调用 unmarshal 方法 [英] JAXB @XmlAnyElement with XmlAdapter does not make a call to the unmarshal method

查看:42
本文介绍了带有 XmlAdapter 的 JAXB @XmlAnyElement 不会调用 unmarshal 方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试解组 XML 并将其映射到 Java POJO.我的 XML 可以有一些用户定义的元素,这些元素可以是随机的,所以我想存储它们.经过研究,我发现我可以使用 @XmlAnyElement(lax=true).我正在尝试将 XMLAdapter@XmlAnyElement 一起使用,但由于某种原因,我的 XMLAdapter 中的方法 unmarshal> 根本没有被调用,因为我无法映射 user-defined 字段.

I am trying to unmarshal the XML and map it to the Java POJO. My XML can have some of the user-defined elements which can be random so I would like to store them. After researching I found that I can use @XmlAnyElement(lax=true). I am trying to use the XMLAdapter along with the @XmlAnyElement but for some reason, the method unmarshal within my XMLAdapter is not being called at all due to which I am unable to map the user-defined fields.

有人可以向我解释如何将 user-defined 字段unmarshal 到我的 Map 以获取 >编组一切正常,我正在使用此处提到的方法.但是 unmarshalling 根本没有被调用,这让我有点困惑.

Can someone please explain to me how to unmarshal the user-defined fields to my Map<String,Object> for the marshalling everything is working fine and I am using the approach mentioned here. But unmarshalling is not being called at all which is bit confusing for me.

以下是我的XML,需要解组.(请注意,namespace 可以是动态的和用户定义的,这可能会在每个 xml 中更改):

Following is my XML which needs to be unmarshalled. (Please note that the namespace can be dynamic and user-defined which may change in every xml):

<Customer xmlns:google="https://google.com">
  <name>Batman</name>
  <google:main>
    <google:sub>bye</google:sub>
  </google:main>
</Customer>

以下是我的 Customer 类,XML 需要解组;

Following is my Customer class to which XML needs to be 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 方法根据此处提供的代码按预期工作.

Following is my XMLAdapter (TestAdapter) class which will be used for marshalling and unmarshalling the user-defined fields. The unmarshalling method is not being called at all. However the marshalling method works as expected based on the code provided 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文件,里面填充了以下内容:

I have used the package-info.java file and it has been filled with following contents:

@jakarta.xml.bind.annotation.XmlSchema(namespace = "http://google.com", elementFormDefault = jakarta.xml.bind.annotation.XmlNsForm.QUALIFIED)
package io.model.jaxb;

我研究了很多,但找不到任何类似的答案.此外,尝试了很多东西,但没有奏效.因此,在这里发布相同的内容并寻找一些建议或解决方法.

I researched a lot but could not find any answer which does something similar. Also, tried a lot of things but none worked. Hence, posting the same here and looking for some suggestion or workaround.

我对解组几乎没有怀疑:

  1. 为什么在解组期间没有调用我的 XMLAdapter TestAdapter.class unmarshal 方法?
  2. 我如何解组可以随命名空间随机出现的 XML 字段?
  3. 是我做错了什么还是我应该做些什么来读取动态显示的命名空间和元素?
  1. Why my XMLAdapter TestAdapter.class unmarshal method is not being called during the unmarshalling?
  2. How can I unmarshal the XML fields which can appear randomly with namespaces?
  3. Am I doing something wrong or is there something else I should do to read the namespaces and elements which appear dynamically?

*** FOLLOWING IS EDITED SECTION BASED ON RESPONSE FROM Thomas Fritsch ****

根据回复,我编辑了我的课程,但仍然没有按预期工作:

Based on the response I have edited my class but still not working as expected:

@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 那么字段 another 将不会在 unmarshalling 期间填充,我需要保持它 @XmlTransient 因为我不想要它在 marshalling 期间同样我为 Map 制作了 @JsonIgnore其他,因为我在解组期间不需要它,但两者相互冲突,无法获得所需的输出.

So what's happening is that if I use @XmlTransient then the field another will not be populated during the unmarshalling and I need to keep it @XmlTransient because I do not want it during the marshalling similarly I have made @JsonIgnore for Map<String, Object> others because I do not need it during the unmarshalling but both things are conflicting with each other and not able to obtain the the required output.

我的主要目标是将 XML 文件转换为 JSON,反之亦然.对于上面提到的 XML 文件,我想在 JSON 中获得以下输出:

My main goal is to convert the XML file to JSON and vice versa. For the above mentioned XML file I would like to obtain the following output in JSON:

{
  "Customer": {
    "name": "BATMAN",
    "google:main": {
      "google:sub": "bye"
    }
  }
}

同样,当我转换这个 JSON 时,我应该得到原始的 XML.

Similarly when I convert this JSON then I should get the original 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);
  }
}

推荐答案

在尝试了很多东西之后,我能够让它工作起来.发布相同的解决方案,以便将来对某人有所帮助.

After trying out a lot of things, I was able to get it working. Posting the solution for the same so it can be helpful to someone in the future.

我使用了Project Lombok,所以你可以看到一些额外的注释,例如@Getter、@Setter 等

I have used Project Lombok so you can see some additional annotations such as @Getter, @Setter, etc

方法一:

正如@Thomas Fritsch 提到的,你必须使用 @XmlAnyElement(lax=true)List 我正在使用 Map.

As mentioned @Thomas Fritsch you have to use the @XmlAnyElement(lax=true) with List<Element> I was using with the Map<String, Object>.

方法二:

您可以继续使用 Map 并使用带有适配器的 @XmlPath(".") 使其工作.在此处发布相同的代码:(我添加了一个额外的字段 age 其他所有内容保持不变)

You can continue to use Map<String,Object> and use @XmlPath(".") with adapter to get it working. Posting the code for the same here: (I have added one additional field age other that everything remain the same)

@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 时,TestAdapter 中的方法unmarshal 将被调用,您可以执行任何您需要的操作.

Following is the TestAdapter.class posting the same for reference. When you unmarhsal the method unmarshal in TestAdapter will get called and you can do anything you need.

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;
}

虽然它适用于特定情况,但我发现使用这种方法存在一个问题.我为此创建了一个新帖子问题.如果我收到有关该问题的任何回复,我将尝试更新代码,使其能够正常工作.

Although it works for specific cases, I am seeing one problem by using this approach. I have created a new post for this issue. If I get any response for that issue then I will try to update the code so it can work correctly.

这篇关于带有 XmlAdapter 的 JAXB @XmlAnyElement 不会调用 unmarshal 方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆