簡體   English   中英

JAXB @XmlAnyElement 與 XmlAdapter 不調用解組方法

[英]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 ,我幾乎沒有疑問:

  1. 為什么在解組期間沒有調用我的XMLAdapter TestAdapter.class unmarshal組方法?
  2. 如何unmarshal組可能隨命名空間隨機出現的 XML 字段?
  3. 我做錯了什么還是我應該做些什么來讀取動態出現的命名空間和元素?
*** 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或數組屬性一起使用(通常與Listorg.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 實際上會調用您的適配器。 適配器的工作是在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 ,其他一切都保持不變)

@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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM