Java 에서 XML 파일 Validation 수행시 기반이 되는 XSD 파일이 여러개이고 XSD 파일관의 관계가 import, include 등으로 복잡할 경우 SchemaFactory.newSchema(Source schema) 호출 Error 가 발생하는 경우가 있다.


다음의 예제로 XSD 파일 Resolve 방법에 대해 알아보자.


다음과 같이 3개의 XSD 파일이 있고 "main.xsd" 파일이 Root 가 되는 XSD 파일로 a.xsd 와 b.xsd 파일을 import 혹은 include 하고 있다.

  • a.xsd
  • b.xsd
  • main.xsd

위와 같은 구조일 경우 일반적으로 다음과 같이 Schema 를 구현한다.

// read the xsd files
FileInputStream mainStream = new FileInputStream("main.xsd");
FileInputStream aStream = new FileInputStream("a.xsd");
FileInputStream bStream = new FileInputStream("b.xsd");
Source[] sources= new Source[] { new StreamSource(mainStream), new StreamSource(aStream), new StreamSource(bStream) }

// create schema
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
Schema schema = factory.newSchema(sources);

// read xml file for validation
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
documentFactory.setNamespaceAware(true);
DocumentBuilder builder = documentFactory.newDocumentBuilder();
Document doc = builder.parse(new StringInputStream(xml)); // parse the XML into a document object

// validation
Validator validator = schema.newValidator();
validator.validate(new DOMSource(doc));
위의 코드로 수행할 경우 main.xsd 내에서 xsd 파일의 참조순서가 a.xsd, b.xsd 순서가 아니라면 9라인의 factory.newSchema(sources) 에서 에러가 발생하게 된다.


위와 같이 xsd 가 여러파일과의 관계를 맺으며 복잡하게 참조관계를 갖을 경우 org.w3c.dom.ls.LSResourceResolver 인터페이스를 구현한 클래스를 SchemaFactory.setResourceResolver 함수로 등록하면 복잡한 참조관계라도 필요한 파일들을 알아서 로딩 할 수 있게 된다.

// create schema
SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
// set resource resolver
factory.setResourceResolver(new ResourceResolver());
Schema schema = factory.newSchema(new StreamSource(new File("main.xsd"));

// read xml file for validation
DocumentBuilderFactory documentFactory = DocumentBuilderFactory.newInstance();
documentFactory.setNamespaceAware(true);
DocumentBuilder builder = documentFactory.newDocumentBuilder();
Document doc = builder.parse(new StringInputStream(xml)); // parse the XML into a document object

// validation
Validator validator = schema.newValidator();
validator.validate(new DOMSource(doc));

class ResourceResolver implements LSResourceResolver {
    private Setresoures = new HashSet();
    @Override
    public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
         if ( resoures.contains( systemId ) ) {
                return null;
        }
        resoures.add(systemId);
        File file = new File(systemId);
        if ( file.exists() ) {
            LSInput input = new Input();
            try {
                input.setByteStream( new FileInputStream( file ) );
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
            return input;
        }
        return null;
    }
}

위의 코드와 같이 LSResourceResolver 인터페이스를 구현한 ResourceResolver 에서 main.xsd 의 참조와 관련한 a.xsd, b.xsd 파일에 대한 정보요청정보를 resolveResource() 함수에서 설정이 가능하다.


 resolveResource() 함수내에서 필요한 a.xsd, b.xsd 파일의 내용이나 위치등을 설정하여 리턴하여주면 main.xsd 내의 참조관계에 따라 필요한 파일정보를 자동으로 요청하여 얻어가게 된다.


Reference: http://stackoverflow.com/questions/3499476/in-java-validate-xml-against-multiple-schemas-located-difference-server-loc

Posted by leechwin