【问题标题】:how can i unmarshall in jaxb and enjoy the schema validation without using an explicit schema file我如何在 jaxb 中解组并在不使用显式模式文件的情况下享受模式验证
【发布时间】:2025-12-07 11:30:01
【问题描述】:

我将 jaxb 用于我的应用程序配置

我觉得我在做一些非常不正当的事情,我正在寻找一种不需要实际文件或此交易的方法。

正如你在代码 I 中看到的:

1.从我的 JaxbContext 中创建一个模式到一个文件中(实际上来自我的类​​注释) 2.设置此架构文件以便在我解组时允许真正的验证

JAXBContext context = JAXBContext.newInstance(clazz);
Schema mySchema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(schemaFile);
jaxbContext.generateSchema(new MySchemaOutputResolver()); // ultimately creates schemaFile   
Unmarshaller u = m_context.createUnmarshaller();
u.setSchema(mySchema);
u.unmarshal(...);

你们有谁知道我如何验证 jaxb 而无需在我的计算机中创建架构文件?

我是否需要创建一个模式进行验证,当我通过 JaxbContect.generateSchema 获得它时它看起来是多余的?

你是怎么做到的?

【问题讨论】:

  • 为什么不从类路径资源中读取架构?
  • 我不确定我是否理解,你能详细说明吗?
  • 我可以从我的类路径加载我的模式文件,但我试图避免创建模式文件,正如你在上面看到的那样,我正在创建文件并在一秒钟后使用它我实际上可以在之后删除它我完成了编组。我在这里错过了一些大图吗?感谢您的帮助

标签: java xsd jaxb


【解决方案1】:

关于上面ekeren的解决方案,在单个线程中使用PipedOutputStream/PipedInputStream并不是一个好主意,以免溢出缓冲区导致死锁。 ByteArrayOutputStream/ByteArrayInputStream 有效,但如果您的 JAXB 类生成多个模式(在不同的命名空间中),您需要多个 StreamSource。

我最终得到了这个:

JAXBContext jc = JAXBContext.newInstance(Something.class);
final List<ByteArrayOutputStream> outs = new ArrayList<ByteArrayOutputStream>();
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);
    // to examine schema: 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 );
m.setSchema(sf.newSchema(sources));
m.marshal(docs, new DefaultHandler());  // performs the schema validation

【讨论】:

  • 谢谢,我也将解决方案更改为 byteArrayInputStream 但我不知道多个模式
  • @seanf。您的解决方案运行良好,但是生成的结果序列与依赖项的顺序不匹配。即: outs[0] 可能依赖于 outs[1]。因此,在创建 newSchema(sources) 时,它会失败,因为它没有找到在使用之前定义的必要依赖项。有没有办法确保顺序与依赖项中所需的顺序相同? (即: outs[1] 在 outs[0] 之前生成)?在*.com/q/8842047/827480 上查看我关于此问题的主题。
【解决方案2】:

我遇到了确切的问题,并在 Apache Axis 2 源代码中找到了解决方案:

protected List<DOMResult> generateJaxbSchemas(JAXBContext context) throws IOException {
    final List<DOMResult> results = new ArrayList<DOMResult>();
    context.generateSchema(new SchemaOutputResolver() {
        @Override
        public Result createOutput(String ns, String file) throws IOException {
            DOMResult result = new DOMResult();
            result.setSystemId(file);
            results.add(result);
            return result;
        }
     });
    return results;
}

在您获得代表模式的 DOMResult 列表后,您需要先将它们转换为 DOMSource 对象,然后才能将它们输入模式生成器。第二步可能如下所示:

Unmarshaller u = myJAXBContext.createUnmarshaller();
List<DOMSource> dsList = new ArrayList<DOMSource>();   
for(DOMResult domresult : myDomList){
    dsList.add(new DOMSource(domresult.getNode()));
}
String schemaLang = "http://www.w3.org/2001/XMLSchema";
SchemaFactory sFactory = SchemaFactory.newInstance(schemaLang);
Schema schema = sFactory.newSchema((DOMSource[]) dsList.toArray(new DOMSource[0]));
u.setSchema(schema);            

【讨论】:

  • 谢谢。它运行良好,没有虚假的 Schema 文件、流等。至于为什么我们需要跳过这些晦涩难懂的圈子只是为了让 JAXB 验证它应该做什么——因为它是 XML!
【解决方案3】:

我相信你只需要在你的解组器上设置一个ValidationEventHandler。像这样的:

public class JAXBValidator extends ValidationEventCollector {
    @Override
    public boolean handleEvent(ValidationEvent event) {
        if (event.getSeverity() == event.ERROR ||
            event.getSeverity() == event.FATAL_ERROR)
        {
            ValidationEventLocator locator = event.getLocator();
            // change RuntimeException to something more appropriate
            throw new RuntimeException("XML Validation Exception:  " +
                event.getMessage() + " at row: " + locator.getLineNumber() +
                " column: " + locator.getColumnNumber());
        }

        return true;
    }
}

在你的代码中:

Unmarshaller u = m_context.createUnmarshaller();
u.setEventHandler(new JAXBValidator());
u.unmarshal(...);

【讨论】:

  • 感谢您的回复,据了解当在没有架构的情况下解组时,您实际上没有验证 XML。例如,@XmlElement(required=true) 注释在没有模式的情况下不会被验证。我错了吗?
  • 我假设你有一个模式开始,并使用 xjc 从它生成你的 JAXB 对象。不是这样吗?即使不是这样,我相信只要所有元素都正确注释,解组器仍然可以验证。
  • 不是我从带注释的类开始,而不是模式。我可以使用 JaxbContext 上的 generateSchema 调用从类中创建模式。问题是,如果我不提供模式解组验证很差。它会警告意外元素,但不会警告元素重复和必需元素。
  • 如果你想验证 XML,你需要一个模式。您可以使用 JAXB 附带的 schemagen 生成一次模式,而不是在运行时从上下文中生成它。
  • 谢谢 Jason,你知道我是否可以生成一个 jaxb 可以验证的内存模式...我真的不想创建需要与我的应用程序打包的模式文件,我有一些遗留代码问题。
【解决方案4】:

如果你使用 maven,使用 jaxb2-maven-plugin 可以帮助你。它在 generate-resources 阶段生成模式。

【讨论】:

    最近更新 更多