【问题标题】:How can I get more information on an invalid DOM element through the Validator?如何通过验证器获取有关无效 DOM 元素的更多信息?
【发布时间】:2014-07-06 21:00:28
【问题描述】:

我正在使用 javax.xml.validation.Validator 类针对 XSD 架构验证内存中的 DOM 对象。每当我从中填充 DOM 的信息中存在某些数据损坏时,我都会在验证期间抛出 SAXParseException

错误示例:

org.xml.SAXParseException: cvc-datatype-valid.1.2.1: '???"??[?????G?>???p~tn??~0?1]' 是不是“hexBinary”的有效值。

我希望有一种方法可以在我的内存 DOM 中找到此错误的位置并打印出有问题的元素及其父元素。我当前的代码是:

public void writeDocumentToFile(Document document) throws XMLWriteException {
  try {
    // Validate the document against the schema
    Validator validator = getSchema(xmlSchema).newValidator();
    validator.validate(new DOMSource(document));

    // Serialisation logic here.

  } catch(SAXException e) {
    throw new XMLWriteException(e); // This is being thrown
  } // Some other exceptions caught here.
}

private Schema getSchema(URL schema) throws SAXException {
  SchemaFactory schemaFactory = 
    SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

  // Some logic here to specify a ResourceResolver

  return schemaFactory.newSchema(schema);
}

我研究了Validator#setErrorHandler(ErrorHandler handler) 方法,但ErrorHandler 接口只让我接触到SAXParseException,它只公开错误的行号和列号。因为我使用的是内存中的 DOM,所以行号和列号都返回 -1。

有没有更好的方法来做到这一点?如果库为我提供了我正在寻找的功能,我真的不想在将它们添加到 DOM 之前手动验证字符串。

我正在使用 JDK 6 update 26 和 JDK 6 update 7,具体取决于此代码的运行位置。

编辑:添加此代码 -

validator.setErrorHandler(new ErrorHandler() {
  @Override
  public void warning(SAXParseException exception) throws SAXException {
    printException(exception);
    throw exception;
  }

  @Override
  public void error(SAXParseException exception) throws SAXException {
    printException(exception);
    throw exception;
  }

  @Override
  public void fatalError(SAXParseException exception) throws SAXException {
    printException(exception);
    throw exception;
  }

  private void printException(SAXParseException exception) {
    System.out.println("exception.getPublicId() = " + exception.getPublicId());
    System.out.println("exception.getSystemId() = " + exception.getSystemId());
    System.out.println("exception.getColumnNumber() = " + exception.getColumnNumber());
    System.out.println("exception.getLineNumber() = " + exception.getLineNumber());
  }
});

我得到了输出:

exception.getPublicId() = null
exception.getSystemId() = null
exception.getColumnNumber() = -1
exception.getLineNumber() = -1

【问题讨论】:

    标签: java xml xml-validation


    【解决方案1】:

    如果您使用 Xerces(Sun JDK 默认),您可以通过 http://apache.org/xml/properties/dom/current-element-node 属性获取验证失败的元素:

    ...
    catch (SAXParseException e)
    {
        Element curElement = (Element)validator.getProperty("http://apache.org/xml/properties/dom/current-element-node");
    
        System.out.println("Validation error: " + e.getMessage());
        System.out.println("Element: " + curElement);
    }   
    

    例子:

    String xml = "<root xmlns=\"http://www.myschema.org\">\n" +
                 "<text>This is text</text>\n" +
                 "<number>32</number>\n" +
                 "<number>abc</number>\n" +
                 "</root>";
    
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    dbf.setNamespaceAware(true);
    Document doc = dbf.newDocumentBuilder().parse(new ByteArrayInputStream(xml.getBytes("UTF-8")));
    Schema schema = getSchema(getClass().getResource("myschema.xsd"));
    
    Validator validator = schema.newValidator();
    try
    {
        validator.validate(new DOMSource(doc));
    }
    catch (SAXParseException e)
    {
        Element curElement = (Element)validator.getProperty("http://apache.org/xml/properties/dom/current-element-node");
    
        System.out.println("Validation error: " + e.getMessage());
        System.out.println(curElement.getLocalName() + ": " + curElement.getTextContent());
    
        //Use curElement.getParentNode() or whatever you need here
    }         
    

    如果您需要从 DOM 中获取行号/列号,this answer 可以解决该问题。

    【讨论】:

    • 嗯,谢谢你的详细回答。我在 JDK 1.6 更新 7 下得到org.xml.sax.SAXNotRecognizedException: Property 'http://apache.org/xml/properties/dom/current-element-node' is not recognized.。我假设访问此属性不需要互联网访问?
    • @Bringer128 我在 Java 1.6.0u25 下运行,它可以工作。它可能是最近添加的 Xerces 属性。如果您必须在旧的 JDK 1.6 下运行以将更高版本的 Xerces 与您的应用程序捆绑在一起,并使用其中的 DocumentBuilderFactory/Validator 实现而不是 JDK 的默认实现,这可能是可能的。
    • 你说得对,它也适用于 Java 1.6.0u26。这似乎是我正在寻找的,谢谢!
    • 在使用 DTD 文件验证 XML 时是否可以这样做?
    【解决方案2】:

    SaxParseException 公开 SystemId 和 PublicId。这是否没有为您提供足够的信息?

    【讨论】:

    • 我已经更新了我的问题。验证器似乎为这两个值返回 null,因此它们没有多大用处。
    猜你喜欢
    • 2015-10-18
    • 2016-12-12
    • 1970-01-01
    • 2015-12-05
    • 2020-04-24
    • 1970-01-01
    • 1970-01-01
    • 2016-01-29
    • 1970-01-01
    相关资源
    最近更新 更多