在 Java 中针对 xsd 的 XML 验证
Posted
技术标签:
【中文标题】在 Java 中针对 xsd 的 XML 验证【英文标题】:XML validation against xsd's in Java 【发布时间】:2012-07-26 02:16:35 【问题描述】:问题: 我们有几个通过 XSLT 生成大量 XML 的服务。我们没有任何 XSD。我已经花时间创建 XSD 并想确认它们是正确的。目前我正在尝试验证 XSD 和 XML 是否正确验证。
问题: 我有一个导入到所有 xsd 中的 xsd(common.xsd)。它还没有公开托管,所以直到最近我才发现将 common.xsd 的完整路径放在 AccountList.xsd 中,我能够走得更远。我现在收到以下信息:
org.xml.sax.SAXParseException;行号:9;列号:70; s4s-att-invalid-value:元素“元素”中的“类型”属性值无效。记录的原因:UndeclaredPrefix:无法将“common:response”解析为 QName:未声明前缀“common”。
我很茫然。我找不到在论坛中询问过的示例或成功的源代码 sn-p。如果能帮助我成功验证我的 xml,我将不胜感激。
common.xsd
<xs:schema version="1.0" elementFormDefault="qualified" attributeFormDefault="unqualified"
xmlns="http://www.myorg.com/xsd/gen_fin"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.myorg.com/xsd/gen_fin">
<xs:complexType name="response">
<xs:sequence>
<xs:element name="code" type="xs:string"/>
<xs:element name="description" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
AccountList.xsd
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xs:schema version="1.0" elementFormDefault="qualified" attributeFormDefault="unqualified"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.myorg.com/xsd/accList"
targetNamespace="http://www.myorg.com/xsd/accList"
xmlns:common="http://www.myorg.com/xsd/gen_fin">
<xs:import namespace="http://www.myorg.com/xsd/gen_fin"
schemaLocation="/home/me/dev/projects/svn/myorg/xsd/src/main/resources/bg/gen_resp/common.xsd"/>
<xs:element name="fundamo">
<xs:complexType>
<xs:sequence>
<xs:element name="response" type="common:response" minOccurs="1" maxOccurs="1"/>
<xs:element name="transaction" type="tns:transaction" minOccurs="0" maxOccurs="1"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="transaction">
<xs:sequence>
<xs:element name="transactionRef" type="xs:string"/>
<xs:element name="dateTime" type="xs:string"/>
<xs:element name="userName" type="xs:string"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
Test.java
final InputStream commonXsdStream = getXsd(BG_GEN_RESP_XSD_PATH, COMMON);
ClassPathResource fullXsdListing = new ClassPathResource(BG_GEN_RESP_XSD_PATH);
File[] allXsds = fullXsdListing.getFile().listFiles();
for (File currentXsd : allXsds)
final int filenameLength = currentXsd.getName().length();
final String filenameSanExt = currentXsd.getName().substring(0, filenameLength - 4);
if (!IGNORE.contains(filenameSanExt))
final InputStream xsltStream = getXslt(BG_GEN_RESP_XSLT_PATH, filenameSanExt);
final InputStream xsdStream = getXsd(BG_GEN_RESP_XSD_PATH, filenameSanExt);
TransformerFactory xmlTransformer = TransformerFactory.newInstance();
Templates xsltTemplate = xmlTransformer.newTemplates(new StreamSource(xsltStream));
final XSLToXMLConvertor converter = new XSLToXMLConvertor();
String generatedXml = converter.getXML(inputData, xsltTemplate);
LOG.info(generatedXml);
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = schemaFactory.newSchema(lnew StreamSource(xsdStream));
Validator validator = schema.newValidator();
validator.validate(new StreamSource(new StringReader(generatedXml)));
/*
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
docBuilderFactory.setNamespaceAware(true);
docBuilderFactory.setValidating(true);
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
docBuilder.parse(new InputSource(new ByteArrayInputStream(generatedXml.getBytes("utf-8"))));
*/
【问题讨论】:
将targetNamespace
添加到AccountList.xsd
有帮助吗?
我试过了,但我收到:错误:src-resolve.4.2:解析组件“事务”时出错。检测到“事务”在命名空间“accList”中,但此命名空间中的组件无法从架构文档“file:/home/me/dev/projects/myorg/xsd/src/main/resources/gen_resp/AccountList”中引用.xsd'。如果这是不正确的命名空间,则可能需要更改“事务”的前缀。如果这是正确的命名空间,则应将适当的“导入”标记添加到“文件:/home/me/dev/projects/myorg/xsd/src/main/resources/gen_resp/AccountList.xsd”。
看起来您添加了targetNamespace="..."
和xmlns:accList="..."
。取出accList
,也许还有普通的xmlns="http://www.myorg.com/xsd/gen_fin"
。您可以编辑原始问题以反映您正在更改的内容吗?
@davidfmatheson 我已经更新了显示添加的“tns”和“targetNamespace”属性的原始问题。我按照你的建议做了,但输出看起来更糟。 :( 你知道有一个在线 web 服务有一个 xsd:import 我可以消费和检查命名空间等吗?非常感谢
您最好从离线开始,只需将 XML 放入本地项目并尝试验证。我在下面修改了我的答案以指定所涉及的所有内容。
【参考方案1】:
定义命名空间和 targetNamespace 通常是个好主意,尽管正如 Petru Gardea 指出的那样,这并不是绝对必要的。这是一个绝对有效的组合:
AccountList.xsd
<xs:schema
version="1.0"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.myorg.com/xsd/accList"
targetNamespace="http://www.myorg.com/xsd/accList"
xmlns:common="http://www.myorg.com/xsd/gen_fin">
<xs:import namespace="http://www.myorg.com/xsd/gen_fin" schemaLocation="common.xsd" />
<xs:element name="fundamo">
<xs:complexType>
<xs:sequence>
<xs:element name="response" type="common:response"
minOccurs="1" maxOccurs="1" />
<xs:element name="transaction" type="tns:transaction"
minOccurs="0" maxOccurs="1" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="transaction">
<xs:sequence>
<xs:element name="transactionRef" type="xs:string" />
<xs:element name="dateTime" type="xs:string" />
<xs:element name="userName" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
common.xsd
<xs:schema
version="1.0"
elementFormDefault="qualified"
attributeFormDefault="unqualified"
xmlns="http://www.myorg.com/xsd/gen_fin"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.myorg.com/xsd/gen_fin">
<xs:complexType name="response">
<xs:sequence>
<xs:element name="code" type="xs:string" />
<xs:element name="description" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:schema>
NewFile.xml(基于该架构):
<tns:fundamo xmlns:p="http://www.myorg.com/xsd/gen_fin"
xmlns:tns="http://www.myorg.com/xsd/accList" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.myorg.com/xsd/accList AccountList.xsd ">
<tns:response>
<p:code>p:code</p:code>
<p:description>p:description</p:description>
</tns:response>
</tns:fundamo>
ValidateXml.java:
import java.io.File;
import java.io.IOException;
import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;
public class ValidateXml
/**
* @param args
*/
public static void main(String[] args)
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
try
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
documentBuilderFactory.setNamespaceAware(true);
DocumentBuilder parser = documentBuilderFactory.newDocumentBuilder();
Document document = parser.parse(new File("NewFile.xml"));
Schema schema = schemaFactory.newSchema(new File("AccountList.xsd"));
Validator validator = schema.newValidator();
validator.validate(new DOMSource(document));
catch (SAXException e)
e.printStackTrace();
catch (ParserConfigurationException e)
e.printStackTrace();
catch (IOException e)
e.printStackTrace();
与“找不到元素的声明”相关的错误通常与 XML 文档不支持命名空间有关。验证您的两个 XSD 的路径是否正确,然后返回到您构建可识别命名空间的 XML 文档的代码块。
【讨论】:
错误答案(虚拟 -1)。从 XSD 的角度来看,不,您不需要目标命名空间。假设导入的位置正确解析,发布的 XSD 完全有效。 现在接收:org.xml.sax.SAXParseException;行号:1;列号:48; cvc-elt.1:找不到元素“fundamo”的声明。以上是关于在 Java 中针对 xsd 的 XML 验证的主要内容,如果未能解决你的问题,请参考以下文章
使用 Java 中的 Xerces 针对 XSD 1.1 进行 XML 验证