JAXB/Moxy @XmlPath(".") 在解组期间与 XMLAdapter 冲突

2022-01-19 00:00:00 xml java jaxb unmarshalling moxy

即使在尝试了很多事情之后也无法找到解决方案,因此在此处发布希望获得一些解决方法或解决此问题.

Was unable to find the solution even after trying many things so posting here hoping to get some workaround or fix for this issue.

基本上,如果 @XmlPath(".") 已在 Map 上使用,并且其上有 XMLAdapter然后它在 unmarshalling 期间失败.marshaling 工作完美,只有 unmarshaling 失败.

Basically, if the @XmlPath(".") has been used on a Map and if there is XMLAdapter on it then it fails during the unmarshalling. The marshaling works perfectly only the unmarshalling fails.

简而言之,我想执行 unmarshalling 这里提到过,但除了 Map 我还会有一个 @XmlElement.所以一个字段用 (Map field) @XmlPath(".") 和另一个 String 字段用 @XmlElement 注释,然后我想执行 解组.

In short, I would like to perform the unmarshalling as mentioned here but along with Map I will have one more @XmlElement. So one field is annotated with (Map field) @XmlPath(".") and another String field with @XmlElement and then I would like to perform unmarshalling.

以下是我尝试unmarshalXML:

<Customer xmlns:google="https://google.com">
  <name>Batman</name>
  <age>2008</age>
  <google:sub1>MyValue-1</google:sub1>
  <google:sub2>MyValue-1</google:sub2>
</Customer>

下面是 Customer.class,它将被 unmarshalled:

@XmlRootElement(name = "Customer")
@XmlType(name = "Customer", propOrder = {"name", "age", "userExtensions"})
@XmlAccessorType(XmlAccessType.FIELD)
@NoArgsConstructor
@Getter
@Setter
@AllArgsConstructor
public class Customer extends Person {

  private String name;
  private String age;

  @XmlPath(".")
  @XmlJavaTypeAdapter(TestAdapter.class)
  @XmlAnyElement
  private Map<String, Object> userExtensions = new HashMap<>();
}

以下是 Main 类,它将 unmarshal:

Following is the Main class which will unmarshal:

public class Unmarshalling {
  public static void main(String[] args)
      throws JAXBException, XMLStreamException, FactoryConfigurationError, IOException, URISyntaxException, ProcessingException {
    final InputStream inputStream = Unmarshalling.class.getClassLoader().getResourceAsStream("customer.xml");
    final XMLStreamReader xmlStreamReader = XMLInputFactory.newInstance().createXMLStreamReader(inputStream);
    final JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
    final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    final Customer customer = unmarshaller.unmarshal(xmlStreamReader, Customer.class).getValue();
    System.out.println("Read XML : " + customer);
  }
}

当我运行这段代码时,我会得到如下输出:

When I run this code I will get the output as:

Read XML : Person(name=Rise Against, age=2000)

它不读取 custom 元素,例如 google:sub1google:sub2.

Its not reading the custom elements such as google:sub1 and google:sub2.

  1. 还有一个与此类似的问题,但其中提到的解决方法对我不起作用.
  2. 我在 MAP 上尝试了 @XmlAnyElement(lax=true),即使这对我不起作用.
  1. There is one more issue similar to this but the workaround mentioned there did not work for me.
  2. I tried the @XmlAnyElement(lax=true) on MAP even that did not work for me.

以下是我用于 marshallingunmarhsallingTestAdapter Map

Following is the TestAdapter that I am using for marshalling and unmarhsalling the Map<String,Object>

public class TestAdapter extends XmlAdapter<Wrapper, Map<String, Object>> {

  @Override
  public Map<String, Object> unmarshal(Wrapper value) throws Exception {
    System.out.println("ADAPTER UNMARSHALLING");
    System.out.println(value.getElements());

    if (value == null) {
      return null;
    }

    //Loop across all elements within ILMD tag
    final Map<String, Object> extensions = new HashMap<>();
    for (Object obj : value.getElements()) {
      Element element = (Element) obj;

      //System.out.println("Node Name : " + element.getNodeName() + " Value : " + element.getTextContent());

      final NodeList children = element.getChildNodes();

      if (children.getLength() == 1) {
        extensions.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));
          }
        }
        extensions.put(element.getNodeName(), child);
      }
    }
    return extensions;
  }

  @SuppressWarnings("unchecked")
  @Override
  public Wrapper marshal(Map<String, Object> v) throws Exception {
    if (v == null) {
      return null;
    }

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

class Wrapper {

  @XmlAnyElement
  List elements;

  public List getElements() {
    return elements;
  }

我尝试了很多东西,但直到现在都没有奏效.有人可以看看这个问题并提供您的解决方案.很高兴提供有关此问题的更多信息,因为我花了将近 10 天的时间寻找答案.

I tried many things but nothing worked till now. Can someone please have a look at this issue and provide your solution. Happy to provide any more info with regards to this issue as I have spent nearly 10 days looking for answers.

我尝试了以下方法:

  1. 又创建了一个变量,并尝试仅在解组期间使用它,但这也失败了.
  2. 尝试再创建一个字段,然后在其上使用 @XmlAnyElement,但这也没有按我的预期工作.
  1. Created one more variable and tried to use it only during unmarshalling but that's also failing.
  2. Tried to create one more field then used the @XmlAnyElement on it but that's also not working as I expected.

尝试了更多的东西,但没有任何帮助.

Tried few more things but nothing helped.

一切都失败了.我试图从 MOXY 调试代码,但我无法在其中了解很多事情.对这个问题的任何帮助都会对我很有帮助.

Everything is failing. I tried to debug the code from MOXY but I am unable to follow many things there. Any help with this issue would be really helpful for me.

推荐答案

我能够通过使用 BeforeMarshalAfterMarshal 方法得到它.在这里发帖,以便将来对某人有所帮助:

I was able to get it by using the BeforeMarshal and AfterMarshal methods. Posting here so it can be helpful to someone in the future:

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, visible = true, property = "isA")
@JsonInclude(Include.NON_NULL)
@JsonIgnoreProperties(ignoreUnknown = true)
@XmlRootElement(name = "extension")
@XmlType(name = "extension", propOrder = {"name", "age", "otherElements"})
@XmlAccessorType(XmlAccessType.FIELD)
@Getter
@Setter
@AllArgsConstructor
@ToString
@NoArgsConstructor
public class Customer {
    @XmlTransient
    private String isA;

    @XmlPath("customer/name/text()")
    private String name;

    @XmlPath("customer/age/text()")
    private String age;

    @XmlAnyElement(lax = true)
    @JsonIgnore
    @XmlPath("customer")
    private List<Object> otherElements = new ArrayList<>();

    @JsonIgnore
    @XmlTransient
    private Map<String, Object> userExtensions = new HashMap<>();

    @JsonAnyGetter
    @JsonSerialize(using = CustomExtensionsSerializer.class)
    public Map<String, Object> getUserExtensions() {
        return userExtensions;
    }

    @JsonAnySetter
    public void setUserExtensions(String key, Object value) {
        userExtensions.put(key, value);
    }

    private void beforeMarshal(Marshaller m) throws ParserConfigurationException {
        System.out.println("Before Marshalling User Extension: " + userExtensions);
        ExtensionsModifier extensionsModifier = new ExtensionsModifier();
        otherElements = extensionsModifier.Marshalling(userExtensions);
        System.out.println("Before Marshalling Final Other Elements " + otherElements);
        userExtensions = new HashMap<>();
    }

    private void afterUnmarshal(Unmarshaller m, Object parent) throws ParserConfigurationException {
        System.out.println("After Unmarshalling : " + otherElements);
        ExtensionsModifier extensionsModifier = new ExtensionsModifier();
        userExtensions = extensionsModifier.Unmarshalling(otherElements);
        System.out.println("Final User Extensions : " + userExtensions);
        otherElements = new ArrayList();
    }
}

完整的答案可以在这里找到:https://stackoverflow.com/a/67923216/7584240

The complete answer can be found here: https://stackoverflow.com/a/67923216/7584240

相关文章