XML是常用的数据交换格式和配置文件格式,本文主要讲解对XML文件的解析与校验方法。
我们先来看个例子:
book.xml
冰与火之歌
乔治马丁
2014
89.00
安徒生童话
安徒生
2004
77.50
think think think
aaa
1997
100.00
解析xml:
public List getBooks(File file) {
List bookList = new ArrayList<>();
Book book;
SAXReader reader = new SAXReader();
try {
Document document = reader.read(file);
Element bookstore = document.getRootElement();
Iterator storeit = bookstore.elementIterator();
while(storeit.hasNext()){
book = new Book();
Element bookElement = (Element) storeit.next();
//遍历bookElement的属性
List attributes = bookElement.attributes();
for(Attribute attribute : attributes){
if(attribute.getName().equals("id")){
String id = attribute.getValue();//System.out.println(id);
book.setId(Integer.parseInt(id));
}
}
Iterator bookit = bookElement.elementIterator();
while(bookit.hasNext()){
Element child = (Element) bookit.next();
String nodeName = child.getName();
if(nodeName.equals("name")){
//System.out.println(child.getStringValue());
String name = child.getStringValue();
book.setName(name);
}else if(nodeName.equals("author")){
String author = child.getStringValue();
book.setAuthor(author);
}else if(nodeName.equals("year")){
String year = child.getStringValue();
book.setYear(Integer.parseInt(year));
}else if(nodeName.equals("price")){
String price = child.getStringValue();
book.setPrice(Double.parseDouble(price));
}
}
bookList.add(book);
book = null;
}
} catch (DocumentException e) {
e.printStackTrace();
}
return bookList;
}
如上是一个书籍清单的xml文件,程序解析这个xml文件就可以得到书籍列表,里面包含书籍的名称、作者、年份和价格:
Book{id=1, name='冰与火之歌', author='乔治马丁', year=2014, price=89.0}
Book{id=2, name='安徒生童话', author='安徒生', year=2004, price=77.5}
Book{id=3, name='think think think', author='aaa', year=1997, price=100.0}
如果有恶意攻击者将上述book.xml文件修改为下面的文件:
冰与火之歌
乔治马丁
2014
89.00
余欢 0.12
安徒生童话
安徒生
2004
77.50
think think think
aaa
1997
100.00
将会导致解析出来的数据有错误:
Book{id=1, name='冰与火之歌', author='余欢', year=2014, price=0.12}
Book{id=2, name='安徒生童话', author='安徒生', year=2004, price=77.5}
Book{id=3, name='think think think', author='aaa', year=1997, price=100.0}
XML文件是一种常用的配置文件和数据交换格式,它是一种标签化的数据格式,可以作为Web Service服务的传输数据,因此对于XML文件的校验是有必要的。
XML本地校验
本地校验大致有两种方式,分别为编写xml的DTD和XSD验证文件。
DTD验证
关于DTD的语法可参考如下两个网站:
https://www.runoob.com/dtd/dtd-tutorial.html
http://www.w3school.com.cn/dtd/index.asp
本文重在讲解校验XML的方法,故具体语法规则不在此处赘述。直接给出针对上述book.xml的dtd验证文件:
book.dtd
在book.xml头部引入该校验文件:
冰与火之歌
乔治马丁
2014
89.00
余欢 0.12
安徒生童话
安徒生
2004
77.50
think think think
aaa
1997
100.00
dtd文件作用:
通过 DTD,您的每一个 XML 文件均可携带一个有关其自身格式的描述。
通过 DTD,独立的团体可一致地使用某个标准的 DTD 来交换数据。
而您的应用程序也可使用某个标准的 DTD 来验证从外部接收到的数据。
您还可以使用 DTD 来验证您自身的数据。
dtd文件只能起到初步校验作用,要想细致地验证xml文件还得用xsd。
XSD验证
关于xsd的语法规则请查询下面网站:
http://www.w3school.com.cn/schema/index.asp
book.xsd
再在book.xml中引入该xsd文件:
冰与火之歌
乔治马丁
2014
89.00
余欢 0.12
安徒生童话
安徒生
2004
77.50
think think think
aaa
1997
100.00
这时,如果本地xml文件不符合制定好的xsd规则,本地xml文件就会报语法错误,从而提前将错误暴露出来,减小代价。这时候有人会说,如果是接收别人传过来的xml文件怎么去校验呢?请接着看下面的文章:
JAVA校验XML
利用本地编辑好的dtd和xsd文件都可以拿来校验xml文件,本文以xsd校验为例来说明:
validateXMLByXSD.java
public String validateXMLByXSD(String xmlFileName, String xsdFileName) {
String result = "";
try {
//创建默认的XML错误处理器
XMLErrorHandler errorHandler = new XMLErrorHandler();
//获取基于 SAX 的解析器的实例
SAXParserFactory factory = SAXParserFactory.newInstance();
//解析器在解析时验证 XML 内容。
factory.setValidating(true);
//指定由此代码生成的解析器将提供对 XML 名称空间的支持。
factory.setNamespaceAware(true);
//使用当前配置的工厂参数创建 SAXParser 的一个新实例。
SAXParser parser = factory.newSAXParser();
//创建一个读取工具
SAXReader xmlReader = new SAXReader();
//获取要校验xml文档实例
Document xmlDocument = (Document) xmlReader.read(new File(xmlFileName));
//设置 XMLReader 的基础实现中的特定属性。核心功能和属性列表可以在 [url]http://sax.sourceforge.net/?selected=get-set[/url] 中找到。
parser.setProperty(
"http://java.sun.com/xml/jaxp/properties/schemaLanguage",
"http://www.w3.org/2001/XMLSchema");
parser.setProperty(
"http://java.sun.com/xml/jaxp/properties/schemaSource",
"file:" + xsdFileName);
//创建一个SAXValidator校验工具,并设置校验工具的属性
SAXValidator validator = new SAXValidator(parser.getXMLReader());
//设置校验工具的错误处理器,当发生错误时,可以从处理器对象中得到错误信息。
validator.setErrorHandler(errorHandler);
//校验
validator.validate(xmlDocument);
XMLWriter writer = new XMLWriter(OutputFormat.createPrettyPrint());
//如果错误信息不为空,说明校验失败,打印错误信息
if (errorHandler.getErrors().hasContent()) {
System.out.println("XML文件通过XSD文件校验失败!");
writer.write(errorHandler.getErrors());
result = "XML文件通过XSD文件校验失败!";
} else {
System.out.println("Good! XML文件通过XSD文件校验成功!");
result = "Good! XML文件通过XSD文件校验成功!";
}
} catch (Exception ex) {
result = "XML文件通过XSD文件校验失败!原因:"+ex.getMessage();
System.out.println("XML文件: " + xmlFileName + " 通过XSD文件:" + xsdFileName + "检验失败。/n原因: " + ex.getMessage());
ex.printStackTrace();
}
return result;
}
UnitTest:
@Test
public void validateXml() throws FileNotFoundException {
String xmlFileName1 = ResourceUtils.getURL("classpath:xml/book.xml").getPath();
String xmlFileName2 = ResourceUtils.getURL("classpath:xml/book_error.xml").getPath();
String xsdFileName = ResourceUtils.getURL("classpath:xml/book.xsd").getPath();
String result1 = xmlService.validateXMLByXSD(xmlFileName1, xsdFileName);
String result2 = xmlService.validateXMLByXSD(xmlFileName2, xsdFileName);
System.out.println(result1);
System.out.println(result2);
}
如果符合你传入的xsd规则文件,则校验会成功,否则会校验失败。
Good! XML文件通过XSD文件校验成功!
XML文件通过XSD文件校验失败!
cvc-complex-type.2.4.d: 发现了以元素 'author' 开头的无效内容。此处不应含有子元素。
Good! XML文件通过XSD文件校验成功!
XML文件通过XSD文件校验失败!
当然你还可以写程序实现利用dtd文件来校验xml。
总结
本文主要是讲解XML文件校验的一般方法,主要分为本地校验和用java程序动态去校验xml文件,希望对读者有用!