当前位置: 首页 > 工具软件 > XSD/e > 使用案例 >

通过xsd校验xml

裴嘉良
2023-12-01

不依赖第三方工具

核心代码

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;

import javax.xml.XMLConstants;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;

public class XmlValidateUtils {
	
	private static Validator initValidator(InputStream xsdInputStream) throws SAXException {
		SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
		Source schemaFile = new StreamSource(xsdInputStream);
		Schema schema = factory.newSchema(schemaFile);
		return schema.newValidator();
	}
	
	private static boolean isValid(Validator validator, InputStream xmlInputStream) throws IOException {
	    try {
	        validator.validate(new StreamSource(xmlInputStream));
	        return true;
	    } catch (SAXException e) {
	        return false;
	    }
	}
	
	private static boolean isValid(Validator validator, String xml) throws IOException {
		try (
				ByteArrayInputStream xmlInputStream = new ByteArrayInputStream(xml.getBytes());
				) {
			return isValid(validator, xmlInputStream);
		}
	}
	
	/**
	 * 验证xml文档是否符合xsd校验
	 * @param xsdInputStream
	 * @param xmlInputStream
	 * @return true 通过校验, false xml有语法错误或未通过校验
	 * @throws SAXException 校验过程出错
	 * @throws IOException 校验过程出错
	 */
	public static boolean isValid(InputStream xsdInputStream, InputStream xmlInputStream) throws SAXException, IOException {
	    return isValid(xsdInputStream, xmlInputStream, null);
	}
	
	/**
	 * 验证xml文档是否符合xsd校验
	 * @param xsdInputStream
	 * @param xml
	 * @return true 通过校验, false xml有语法错误或未通过校验
	 * @throws SAXException 校验过程出错
	 * @throws IOException 校验过程出错
	 */
	public static boolean isValid(InputStream xsdInputStream, String xml) throws SAXException, IOException {
	    return isValid(xsdInputStream, xml, null);
	}
	
	/**
	 * 验证xml文档是否符合xsd校验
	 * @param xsdInputStream
	 * @param xmlInputStream
	 * @param errorHandler xml文档warning、error、fatalError的回调接口
	 * @return true 由errorHandler的方法实现决定,false xml校验未通过
	 * @throws SAXException 校验过程出错
	 * @throws IOException 校验过程出错
	 */
	public static boolean isValid(InputStream xsdInputStream, InputStream xmlInputStream, ErrorHandler errorHandler) throws SAXException, IOException {
	    Validator validator = initValidator(xsdInputStream);
	    if (errorHandler != null) {
	    	validator.setErrorHandler(errorHandler);
		}
	    
	    return isValid(validator, xmlInputStream);
	}
	
	/**
	 * 验证xml文档是否符合xsd校验
	 * @param xsdInputStream
	 * @param xml
	 * @param errorHandler xml文档warning、error、fatalError的回调接口
	 * @return true 由errorHandler的方法实现决定,false xml校验未通过
	 * @throws SAXException 校验过程出错
	 * @throws IOException 校验过程出错
	 */
	public static boolean isValid(InputStream xsdInputStream, String xml, ErrorHandler errorHandler) throws SAXException, IOException {
	    Validator validator = initValidator(xsdInputStream);
	    if (errorHandler != null) {
	    	validator.setErrorHandler(errorHandler);
		}
	    return isValid(validator, xml);
	}

}

说明

  1. 如果不传入 errorHandler 参数,则使用JDK默认的 DraconianErrorHandler,当xml严重错误时执行 fatalError 方法,当xml内容不符合xsd规则时执行 error 方法,此时都会抛出 SAXParseException 异常,由上面的 private static boolean isValid(Validator validator, InputStream xmlInputStream) throws IOException 方法捕获,返回false校验未通过;当校验通过时,不执行 error 方法和 fatalError 方法,不抛出 SAXParseException 异常,返回true。
package com.sun.org.apache.xerces.internal.jaxp.validation;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

/**
 * {@link ErrorHandler} that throws all errors and fatal errors.
 *
 * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
 */
final class DraconianErrorHandler implements ErrorHandler {

    /**
     * Singleton instance.
     */
    private static final DraconianErrorHandler ERROR_HANDLER_INSTANCE
        = new DraconianErrorHandler();

    private DraconianErrorHandler() {}

    /** Returns the one and only instance of this error handler. */
    public static DraconianErrorHandler getInstance() {
        return ERROR_HANDLER_INSTANCE;
    }

    /** Warning: Ignore. */
    public void warning(SAXParseException e) throws SAXException {
        // noop
    }

    /** Error: Throws back SAXParseException. */
    public void error(SAXParseException e) throws SAXException {
        throw e;
    }

    /** Fatal Error: Throws back SAXParseException. */
    public void fatalError(SAXParseException e) throws SAXException {
        throw e;
    }

}
  1. 如果传入自定义的 errorHandler 参数,则使用自定义的 ErrorHandler。
    如:自定义的 XmlErrorHandler 类,当xml严重错误时执行 fatalError 方法,当xml内容不符合xsd规则时执行 error 方法,此时未抛出异常,而是将异常对象 SAXParseException 加入到对应的 List,上面的 private static boolean isValid(Validator validator, InputStream xmlInputStream) throws IOException 方法不能捕获 SAXParseException 异常,返回true;当捕获到 SAXParseException 异常时,表示校验程序出错,返回false。
    因此这里不能根据方法的返回值是true或false来判断是否校验通过。
import java.util.ArrayList;
import java.util.List;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;

public class XmlErrorHandler implements ErrorHandler {

	private List<SAXParseException> warningExceptions;
	
	private List<SAXParseException> errorExceptions;
	
	private List<SAXParseException> fatalErrorExceptions;

    public XmlErrorHandler() {
        this.warningExceptions = new ArrayList<>();
        this.errorExceptions = new ArrayList<>();
        this.fatalErrorExceptions = new ArrayList<>();
    }

    public List<SAXParseException> getWarningExceptions() {
		return warningExceptions;
	}

	public List<SAXParseException> getErrorExceptions() {
		return errorExceptions;
	}

	public List<SAXParseException> getFatalErrorExceptions() {
		return fatalErrorExceptions;
	}

	@Override
    public void warning(SAXParseException exception) {
		warningExceptions.add(exception);
    }

    @Override
    public void error(SAXParseException exception) {
    	errorExceptions.add(exception);
    }

    @Override
    public void fatalError(SAXParseException exception) {
    	fatalErrorExceptions.add(exception);
    }

}

使用

public class JarUtils {
	/**
	 * 获取项目里的资源输入流
	 * @param sourcePath 项目编译后classes目录下的文件路径, 如application.properties、com/xxx/util/JarUtils.class
	 * @return null 如果未找到文件
	 */
	public static InputStream getResourceAsStream(String sourcePath) {
		return JarUtils.class.getClassLoader().getResourceAsStream(sourcePath);
	}
}

使用JDK默认的 errorHandler

public static void main(String[] args) throws SAXException, IOException {
	String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n"
			+ "<individual>\r\n"
			+ "    <name>student</name>\r\n"
			+ "    <address>\r\n"
			+ "        <zip>00001</zip>\r\n"
			+ "        <city>New York</city>\r\n"
			+ "    </address>\r\n"
			+ "</individual>";
	InputStream xsdInputStream = JarUtils.getResourceAsStream("xmlschema/full-person.xsd");
	if (xsdInputStream == null) {
		return;
	}
	try {
		boolean valid = isValid(xsdInputStream, xml);
		System.out.println("校验结果:" + valid);
	} finally {
		xsdInputStream.close();
	}
	
}

使用自定义的 errorHandler

/**
 * 验证xml文档是否符合xsd校验
 * @param xsdPath
 * @param xml
 * @throws RuntimeException 校验程序出错或语法错误
 * @throws IllegalArgumentException 校验未通过
 */
public static void validate(String xsdPath, String xml) {
	InputStream xsdInputStream = JarUtils.getResourceAsStream(xsdPath);
	if (xsdInputStream == null) {
		throw new NullPointerException("未找到" + xsdPath + "校验文档");
	}
	try {
		XmlErrorHandler errorHandler = new XmlErrorHandler();
		try {
			boolean valid = isValid(xsdInputStream, xml, errorHandler);
			if (!valid) {
				if (!errorHandler.getFatalErrorExceptions().isEmpty()) {
					throw new IllegalArgumentException("xml:" + xml + "语法错误", errorHandler.getFatalErrorExceptions().get(0));
				}
				throw new RuntimeException("校验程序出错");
			}
		} catch (Exception e) {
			throw new RuntimeException("校验程序出错", e);
		}
		if (!errorHandler.getErrorExceptions().isEmpty()) {
			throw new IllegalArgumentException("xml:" + xml + "校验未通过", errorHandler.getErrorExceptions().get(0));
		}
	} finally {
		try {
			xsdInputStream.close();
		} catch (IOException e) {
		}
	}
}

public static void main(String[] args) {
	String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\r\n"
			+ "<individual>\r\n"
			+ "    <name>student</name>\r\n"
			+ "    <address>\r\n"
			+ "        <zip>00001</zip>\r\n"
			+ "        <city>New York</city>\r\n"
			+ "    </address>\r\n"
			+ "</individual>";
	try {
		validate("xmlschema/full-person.xsd", xml);
		System.out.println("校验通过");
	} catch (Exception e) {
		e.printStackTrace();
	}
	
}

参考文档

https://www.baeldung.com/java-validate-xml-xsd

 类似资料: