如何使用 Jakarta XML Unmarshaller 解组没有命名空间的 XML?

问题描述 投票:0回答:1

我正在尝试使用

jakarta.xml.bind.Unmarshaller
将 XML 解组为 Java 对象,但我的 XML 不包含某些默认名称空间,因为它们已经是 XSD 的一部分。如何明确提及
Unmarshaller
以忽略这些默认命名空间或在运行时将它们添加到
XMLStreamReader
以便 Unmarshaller 可以使用它们?

以下是未能解组到 Java 对象的 XML:

<foo>
    <bar>Hello</bar>
    <test:one>Hello ONE</test:one>
</foo>

如果我更改为这个,那么它将按预期工作:

<foo>
    <bar>Hello</bar>
    <test:one xmlns:test="https://test.com">Hello ONE</test:one>
</foo>

我在 StackOverflow 上查看了一些示例,但提供的方法不起作用。我不想使用额外的库(例如 Sax),而是想在 XMLStreamReader 级别或使用 Unmarshaller 进行处理。

如何实现这一目标?

以下是对 XML 进行解组的方法:

主班:

import jakarta.xml.bind.JAXBContext;
import jakarta.xml.bind.JAXBException;
import jakarta.xml.bind.Unmarshaller;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.InputStream;

public class Main {
    public static void main(String[] args) throws XMLStreamException, JAXBException {
        final XMLInputFactory inputFactory = XMLInputFactory.newInstance();
        inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE);
        inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE);
        final InputStream inputStream = Main.class.getResourceAsStream("/SampleXML.xml");
        final XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(inputStream);
        final JAXBContext jaxbContext = JAXBContext.newInstance("io.convert.test", Thread.currentThread().getContextClassLoader());
        final Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
        xmlStreamReader.next();

        while (xmlStreamReader.hasNext()) {
            Object event = unmarshaller.unmarshal(xmlStreamReader, Foo.class).getValue();
            System.out.println(event.toString());
        }
    }
}

Foo.class:


import jakarta.xml.bind.annotation.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "foo")
@XmlType(
        name = "Foo",
        propOrder = {
                "bar",
                "anyElements"
        },
        factoryClass = ObjectFactory.class,
        factoryMethod = "createFoo")
@ToString(callSuper = true)
public class Foo {
    private String bar;
    
    @XmlAnyElement(lax = true)
    private List<Object> anyElements;

    void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("Before Unmarshaller Callback");
        System.out.println(parent);
    }

    void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("Before Unmarshaller Callback");
        System.out.println(parent);
    }
}

ObjectFactory.class:

import jakarta.xml.bind.annotation.XmlRegistry;

@XmlRegistry
public final class ObjectFactory {
  private ObjectFactory() {}
  public static Foo createFoo() {
    return new Foo();
  }
}

在没有命名空间的情况下使用XML时发生错误:

Exception in thread "main" jakarta.xml.bind.UnmarshalException
 - with linked exception:
[Exception [EclipseLink-25004] (Eclipse Persistence Services - 4.0.2.v202306161219): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document
Internal Exception: javax.xml.stream.XMLStreamException: ParseError at [row,col]:[3,15]
Message: http://www.w3.org/TR/1999/REC-xml-names-19990114#ElementPrefixUnbound?test&test:one]
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.handleXMLMarshalException(JAXBUnmarshaller.java:1132)
    at org.eclipse.persistence.jaxb.JAXBUnmarshaller.unmarshal(JAXBUnmarshaller.java:471)
    at io
.convert.test.Main.main(Main.java:24)
Caused by: Exception [EclipseLink-25004] (Eclipse Persistence Services - 4.0.2.v202306161219): org.eclipse.persistence.exceptions.XMLMarshalException
Exception Description: An error occurred unmarshalling the document

基于很少的响应,我尝试添加附加类来返回名称空间,但这也不起作用 NamespaceIgnoringXMLStreamReader.clas

import javax.xml.stream.XMLStreamReader;
import javax.xml.stream.util.StreamReaderDelegate;

public class NamespaceIgnoringXMLStreamReader extends StreamReaderDelegate {
    public NamespaceIgnoringXMLStreamReader(XMLStreamReader reader) {
        super(reader);
    }
    @Override
    public String getAttributeNamespace(int arg0) {
        return "";
    }
    @Override
    public String getNamespaceURI() {
        return "";
    }
}

然后在 Main.class 中使用这些流:

final XMLStreamReader xmlStreamReader = inputFactory.createXMLStreamReader(inputStream);
NamespaceIgnoringXMLStreamReader namespaceIgnoringReader = new NamespaceIgnoringXMLStreamReader(xmlStreamReader);

我尝试使用以下代码添加

package-info.java
,但仍然没有命名空间的解组对我不起作用:

@XmlSchema(elementFormDefault = XmlNsForm.UNQUALIFIED)
package io.openepcis.convert.test;

import jakarta.xml.bind.annotation.XmlNsForm;
import jakarta.xml.bind.annotation.XmlSchema;
java xml jpa jaxb moxy
1个回答
0
投票

这也让我绞尽脑汁,因为我有一个发布者将来自 XML 片段的消息拼凑在一起,其中一些片段来自 Oracle DB,它吐出 XMLType,并且这些片段不存储在具有命名空间的 DB 中。它曾经可以与 javax 一起使用,但现在不行了。

最简单的答案是,如果您有一个用于生成所有 @XmlType 类的 .xsd 文件,请将 elementFormDefault 设置为“unqualified”。这将允许您使用将声明的类型作为其参数之一的解组器。

这里有一些带有模式片段的示例:https://tomee.apache.org/jakartaee-10.0/javadoc/index.html?jakarta/xml/bind/Unmarshaller.html

如果您的架构将 elementFormDefault 设为“合格”并且您无法更改它(这就是我的情况),那么我仍在寻找答案。

© www.soinside.com 2019 - 2024. All rights reserved.