如何使用不同的名称空间解组 xml

How to unmarshall xml with different namespaces

我使用 JAXB 2.0 并希望解组 xml 出现验证错误。例如,如果缺少某些元素或属性。 这是我要解析的xml:

<?xml version="1.1" encoding="utf-8"?>
<package version="2.0" xmlns="http://www.idpf.org/2007/opf">
  <metadata xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:opf="http://www.idpf.org/2007/opf">
    <dc:title>Title</dc:title>
  </metadata>
</package>

我的模型类:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "package")
public class Package {
    @XmlElement(required = true)
    public Metadata metadata;

    @XmlAttribute(name = "version", required = true)
    public String version;
}

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "metadata")
public class Metadata {

    @XmlElement(namespace = "http://purl.org/dc/elements/1.1/", required = true)
    public String title;
}

package-info.java还有一个注解:

@javax.xml.bind.annotation.XmlSchema(namespace = "http://www.idpf.org/2007/opf", elementFormDefault = javax.xml.bind.annotation.XmlNsForm.QUALIFIED)

解组代码:

    JAXBContext jc = JAXBContext.newInstance(Package.class);
    Unmarshaller unmarshaller = jc.createUnmarshaller();

    final List<ByteArrayOutputStream> outs = new ArrayList<>();
    jc.generateSchema(new SchemaOutputResolver(){
        @Override
        public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException {
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            outs.add(out);
            StreamResult streamResult = new StreamResult(out);
            streamResult.setSystemId("");
            return streamResult;
        }});
    StreamSource[] sources = new StreamSource[outs.size()];
    for (int i=0; i<outs.size(); i++) {
        ByteArrayOutputStream out = outs.get(i);
        System.out.append(new String(out.toByteArray()));
        sources[i] = new StreamSource(new ByteArrayInputStream(out.toByteArray()),"");
    }
    SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    Schema schema = sf.newSchema(sources);
    unmarshaller.setSchema(schema);

    unmarshaller.setEventHandler(event -> {
        System.out.append(event.toString());
        return true;
    });
    Opf file = (Opf) unmarshaller.unmarshal(opfFile);

它生成该架构:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema elementFormDefault="qualified" version="1.0" targetNamespace="http://www.idpf.org/2007/opf" xmlns:tns="http://www.idpf.org/2007/opf" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:ns1="http://purl.org/dc/elements/1.1/">
  <xs:import namespace="http://purl.org/dc/elements/1.1/"/>
  <xs:element name="metadata" type="tns:metadata"/>
  <xs:element name="package" type="tns:package"/>
  <xs:complexType name="package">
    <xs:sequence>
      <xs:element ref="tns:metadata"/>
    </xs:sequence>
    <xs:attribute name="version" type="xs:anySimpleType" use="required"/>
  </xs:complexType>
  <xs:complexType name="metadata">
    <xs:sequence>
      <xs:element ref="ns1:title"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" targetNamespace="http://purl.org/dc/elements/1.1/" xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xs:element name="title" type="xs:string"/>
</xs:schema>

并且在解组期间抛出错误:org.xml.sax.SAXParseException: src-resolve: 无法将名称 'ns1:title' 解析为 (n) 'element declaration' 组件。

我应该如何注释我的 类 来解析这个 xml 文件?

你必须亲自动手处理资源解析器;以下是工作代码:

    JAXBContext jc = JAXBContext.newInstance(Package.class);
Unmarshaller unmarshaller = jc.createUnmarshaller();

final Map<String, ByteArrayOutputStream> outs = new HashMap<String, ByteArrayOutputStream>();

jc.generateSchema(new SchemaOutputResolver(){
    @Override
    public Result createOutput(String namespaceUri, String suggestedFileName) throws IOException{
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        outs.put(suggestedFileName, out);
        StreamResult streamResult = new StreamResult(out);
        streamResult.setSystemId(suggestedFileName);
        return streamResult;
    }});
StreamSource[] sources = new StreamSource[outs.size()];
int i = 0;
for (Map.Entry<String, ByteArrayOutputStream> entry: outs.entrySet()) {
    System.out.append(new String(entry.getValue().toByteArray()));
    sources[i++] = new StreamSource(new ByteArrayInputStream(entry.getValue().toByteArray()), entry.getKey());
}
SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
sf.setResourceResolver(new LSResourceResolver(){
    @Override
    public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI){
        ByteArrayOutputStream bout = outs.get(systemId);
        if(bout!=null){
            return new DOMInputImpl(publicId, systemId, baseURI, new ByteArrayInputStream(bout.toByteArray()), null);
        }else
            return null;
    }
});
Schema schema = sf.newSchema(sources);
unmarshaller.setSchema(schema);
unmarshaller.setEventHandler(new ValidationEventHandler(){
    @Override
    public boolean handleEvent(ValidationEvent event){
        System.out.append(event.toString());
        return true;
    }
});

Object obj = unmarshaller.unmarshal(new File("package.xml"));