Java XML SchemaFactory, Schema, Validator, validate()

2023. 1. 12. 20:56Java

프로젝트 내 src 내 xml 패키지 내 test03.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema">
    <xsd:element name="제목">
        <xsd:complexType>
            <xsd:sequence>
                <xsd:element name="가격" type="xsd:positiveInteger"/>

                <xsd:element name="사이즈" type="xsd:double"/>
                
                <xsd:element name="이미지">
                    <xsd:complexType>
                        <xsd:attribute name="src" type="xsd:anyURI"/>
                    </xsd:complexType>
                </xsd:element>
                
                <xsd:element name="컬러">
                    <xsd:complexType>
                        <xsd:attribute name="빨강색" type="xsd:unsignedByte"/>
                        <xsd:attribute name="노란색" type="xsd:unsignedByte"/>
                    </xsd:complexType>
                </xsd:element>
            </xsd:sequence>
        </xsd:complexType>
    </xsd:element>
</xsd:schema>

프로젝트 내 src 내 xml 패키지 내 test031.xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
<제목 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:noNamespaceSchemaLocation="file:/D:/javaddazua/java_xml/src/xml/test03.xsd">
 -->
<!-- 스키마문서 지운상태로 만들기 지난번에 만든 ValidateXmlApp을 수행해본다 이러한
문서들이 실제로 많다 응용프로그램 자체에서 xsd 를 만들어서 유효성 검사 패키지를 알아야 한다 -->
<제목 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <가격>1000</가격>
    <사이즈>65.45</사이즈>
    <이미지 src="/xml/test/book.gif"/>
    <컬러 빨강색="201" 노란색="255"/>
</제목>

아래글 원문 웹페이지로 이동

xml에서 xmlns의 의미

 
xmlns="*" 해당 애플리케이션이 실행되는 디렉토리에서 그 네임스페이스를 사용
 
 컴포넌트를 찾는다는 의미입니다. 만약 xmlns="ok.gosu.*"로 하고 아래에서 
 
<gosu:MyComp>라고 하면 MyComp라는 컴포넌트를 /ok/gosu/MyComp.mxml (또는 
 
MyComp.as)를 찾게 됩니다. 그냥 xmlns="*"로하면 현재 디렉토리에서 MyComp.mxml을 찾
 
겠죠.
 
 

 

"xsi"는 "http://www.w3.org/2001/XMLSchema-instance"를 가리키는 접두어
 
스키마 문서는 네임스페이스(namespace)로 조직화된다: 모든 이름이 달린 스키마 구성 요소는 target namespace에 속하며, 그 target namespace는 스키마 문서 전체의 속성 중 하나이다. 스키마 문서는 네임스페이스가 같은 다른 스키마 문서를 include할 수 있다. 또한 다른 네임스페이스의 스키마 문서를 import할 수 있다.
어떤 인스턴스 문서를 어떤 스키마에 대하여 유효성 검증할 때(assessment라 한다), 유효성 검증에 사용될 스키마는 유효성 검증 엔진에게 인자로 주어질 수도 있고, 인스턴스 문서 자체에서 직접 참조될 수도 있다. 인스턴스 문서가 참조할 때는 두 가지 특별한 애트리뷰트를 사용하는데, xsi:schemaLocation과 xsi:noNamespaceSchemaLocation이 그것이다. 관습적으로 "xsi"는 "http://www.w3.org/2001/XMLSchema-instance"를 가리키는 접두어로 쓰인다.)
XML Schema 문서는 대개 확장자로 ".xsd"를 갖는다. XSD에 대한 고유의 Internet Media Type은 아직 등록되지 않았다. 따라서 RFC 3023 에 따라 "application/xml" 혹은 "text/xml"을 사용해야 한다.

[출처] xmlns, xmlns:xsi 이란?|작성자 동시사랑

 

============================================================================================

프로젝트 내 src 내 java_xml 패키지 내 ValidateXmlApp.java

package java_xml;

import org.xml.sax.InputSource;

public class ValidateXmlApp {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		boolean isValid = false;
		
		InputSource srcXml = new InputSource("src/xml/test031.xml");
		isValid = ValidateApp.validateXml(srcXml);
		
		//유효성 검사 후의 문서 처리코드
		if(isValid) {
			System.out.println("유효한 문서입니다.");
		} else {
			System.out.println("유효하지 않는 문서 입니다.");
		}
	}

}

============================================================================================

프로젝트 내 src 내 java_xml 패키지 내 ValidateXmlXsd.java

package java_xml;

import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;

import org.xml.sax.InputSource;

public class ValidateXmlXsd {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		// 스키마 문서가 없으므로 스키마 문서를 로딩해서 동적으로 유효성 검사위한 패키지 필요
		/*
		 * 동적으로 스키마문서를 로딩해서 XML 문서의 유효성 검사를 하기위해서는
		 * javax.xml.validation 패키지를 이용한다.
		 * 
		 * 세 개의 클래스로 유효성 검사하기
		 * - SchemaFactory : 동적으로 XML스키마 문서 객체를 생성
		 * - Schema : 동적으로 생성된 XML 스키마 객체
		 * - Validator : 스키마 객체를 가지고 유효성 검사를 하는 객체
		 * 
		 * ValidateApp.java 클래스를 보강하러가기
		 * 
		 * 스키마팩토리 생성
		 * --> SchemaFactory.newInstance(String schemaLanguage);
		 * 
		 * 스키마 생성 세가지 형태
		 * 단일스키마문서
		 * --> schemaFactory.newSchema(File schema);
		 * --> schemaFactory.newSchema(Source schema);
		 * 여러개의어떤스키마문서로딩할경우
		 * --> schemaFactory.newSchema(Source[] schema);
		 * 
		 * Validator 생성(유효성검사하는)
		 * -->schema.newValidator();
		 * 
		 * 최종으로 등록하기
		 * 에러이벤트핸들러 처리할 수 있도록 등록해주는 과정이 필요하다
		 * 에러핸들러 구현 객체를 등록
		 * --> setErrorHandler(new ValidateApp());
		 * 
		 * 마지막으로 검사 유효성 검사
		 * --> Validator.validate(Source src);
		 * 
		 * ValidateApp.java 로 가서 메서드 작성하기
		 * validateXmlSchema 메서드를 불러오기만 하면 된다
		 */

		boolean isValid = false;

		Source srcXml = new StreamSource("src/xml/test031.xml");
		//로딩해서 소스객체를 하나 만들어준것
		Source[] srcXsd = new Source[] {
				new StreamSource("src/xml/test03.xsd")
		};
		
		isValid = ValidateApp.validateXmlSchema(srcXml, srcXsd);

		//유효성 검사 후의 문서 처리코드
		if(isValid) {
			System.out.println("유효한 문서입니다.");
		} else {
			System.out.println("유효하지 않는 문서 입니다.");
		}
	}

}
// XML Path Language(XPath)
// Path 경로, 각각의 태그들로 이루어져 있죠. XPath를 이용하면 태그를 쉽게 접근할수있다
// XPath 랭귀지로 경로를 지정해서 찾아갈 수 있다. 속성이나 엘리먼트를 접근할 수 있다.
// 접근하기 위해서 만들어놓은 언어
// XML문서에서 특정 엘리먼트나 속성에 접근하기 위해 경로를 지정하는 언어
// XSLT언어와 Xpointer언어에서 사용
// XPath가 없다면. XSLT와 XPath로 이루어진 언어.
// XSL을 사실상 작성할 수가 없다. 필수적인 요소 언어 XPath으로 XSL문서의 필수적인 언어이다
// 절대경로 "/"로 시작
// 상대 경로 ".", ".." 를 사용

위에서

Source[] srcXsd = new Source[] { new StreamSource("src/xml/test03.xsd") };

부분은 많이 써보지 않은 형태인데

배열 선언시에

int[] list = new int[]{10,20,30,40,50};

와 같이

배열을 선언하면서 동시에 배열을 생성하는 모양새이다.

 

밑에 설명에서 XML Path Language(XPath)를 다음 코드에서 볼 수 있다.

======================================================

XpathTest.java

package java_xml;

import javax.xml.xpath.*;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

public class XpathTest {

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		/*
		 * 자바에서 제공하고있는 패키지
		 * 자바에서는 Xpath로 XML문서를 검색하기 위해서
		 * javax.xml.xpath패키지에서 제공하는 XPath 인터페이스를 구현한 클래스를 생성해야한다.
		 * 
		 * XPathFactory xpathFactory = XPathFactory.newInstance();
		 * XPath xpath = xpathFactory.newXPath();
		 * 
		 * XPath에서 제공하는 evaluate() 메소드를 이용해서 검색가능
		 * 
		 * 반환타입이 문자열인 경우.우리가검색하고자하는것이문자열일경우와 객체타입으로반환하는타입으로 두가지
		 * 스트링:단일값반환하는경우,해당속성값이라든지.책이라는엘리먼트안에제목이있다고가정하면
		 * 제목이라는엘리먼트.내용부.컨텐트.문자데이타.엘리먼트에 대한 내용을 가져오거나 할때
		 * 책의 여러가지 속성 중 분류다그럼 이 속성에대한 값을 갖고오고자할때 해당값,단일값을 가져올수있다.
		 * 
		 * 오브젝트형 반환하는 경우:검색대상자체가 노드이다.노드(태그)라든지 노드리스트(여러개의엘리먼트).
		 * 책에 엘리먼트 수만큼 가져올수있는데 이땐 오브젝트형태로반환하자는거죠
		 * 
		 * expression은 XPath에 대한 표현식이다.
		 * 절대경로로 표시한다면 /는 최상위루트엘리먼트
		 * InputSource 일반 XML 문서의 위치 뚀는 돔 또는 스트림형태
		 * 
		 * Object item은 노드형태로 입력하고자 할때 오브젝트 타입으로 사용
		 * 
		 * QName은 리턴타입이다. 검색대상자체가 노드이므로 해당 노드 타입.여러개거나
		 * 단일 엘리먼트일 경우 그 엘리먼트에 대한 객체타입을 지정해 주는것. 자바상수.
		 * 리턴하게될 큐네임 타입을 지정하는 인자다
		 * 
		 * 큐네임은 각엘리먼트에서 접두어와 해당 엘리먼트 이름을 큐네임이라 한다
		 * 큐네임에 대한 해당 엘리먼트에 대한 리턴타입이 된다.
		 * 
		 * String evaluate(String expression, InputSource source)
		 * String evaluate(String expression, Object item)
		 * Object evaluate(String expression, InputSource source, QName returnType)
		 * Object evaluate(String expression, Object item, QName returnType)
		 * 
		 * 큐네임 객체와 형변환 가능한 자바 객체형
		 * Qname 객체
		 * java.xml.xpath패키지안에 XPathConstants라고 하는 상수를 이용해서 큐네임
		 * 객체를 표기한다
		 * 
		 * evaluate로 해당 xml문서를 검색할텐데...
		 * 
		 * evaluate의 반환타입이 된다.
		 * XPathConstants.BOOLEAN -> java.lang.Boolean
		 * XPathConstants.NODE	  -> org.w3c.dom.Node
		 * XPathConstants.NODESET 여러개의 엘리먼트일 경우 리스트형태로 값을 보냄 -> org.w3c.dom.NodeList로 형변환해줘야
		 * XPathConstants.NUMBER 각 엘리먼트(큐네임)의 수 -> java.lang.Double
		 * XPathConstants.STRING 문자열형태로 보내줄수있다 -> java.lang.String
		 * 
		 * 
		 */
		
		//XPath 객체 생성
		XPathFactory xfactory = XPathFactory.newInstance();
		XPath xpath = xfactory.newXPath();
		
		//XML 원본에 대한 InputSource 객체 생성
		InputSource xmlSrc = new InputSource("src/xml/test02.xml");
		
		System.out.println("==== 아이디(책의속성)를 이용한 검색 ====");
		//id는 속성 책의 속성. 도서리스트가 최상위 루트 엘리먼트.
		//도서리스트 전에 최상위 루트 엘리먼트는 슬래쉬 / 로 시작하고
		//슬래쉬 밑에 도서리스트가 있다 도서리스트 밑에 책이있다
		//책이 하나 둘 세개 있다. 도서리스트에는
		//똑같은 자식이 세명있는 거다. 책 밑에 여러개 자식들있다
		//책에 대한 각각의 제목,저자,출판사,가격,이미지,소개가 있다.
		//책에는 분류와 id라는 속성이 있다. 노드(돔트리)=엘리먼트, 같은 레벨의 자식들이 노드셋이다(자바에서는 노드셋을 노드리스트로 변환한다)
		//책을 표현하면 밑에처럼 XPath 표현식이 된다
		//	/도서리스트/책
		// 해당 속성에 대한 표시는 대괄호 [@속성명]
		String title = xpath.evaluate("/도서리스트/책/제목[../@id='book1']", xmlSrc);
		
		System.out.println(title);
		
		System.out.println("==== 아이디로 책 엘리먼트 찾아오기 ====");
		//이경우 엘리먼트가 반환되니 반환타입은 오브젝트가 된다, 각 엘리먼트=노드
		//반환타입이 노드이다
		Node nBook = (Node)xpath.evaluate("/도서리스트/책[@id='book2']", xmlSrc, XPathConstants.NODE);	//아이디가 book2인 책 엘리먼트를 구하겠다 반환타입을 맞춘다.형변환
		//엘리먼트객체 책
		Element bookElement = (Element) nBook;
		//이 nBook에 대한 해당 속성을 구해오면 두번째 엘레먼트에 대한 분류값이 소설이므로 소설인지 아닌지 확인하면 두번째 노드를 구해온걸 정확히 확인가능
		System.out.println(bookElement.getAttribute("분류"));
		
		//책이 총 엘리먼트 수가 몇개인가
		System.out.println("===== 책 엘리먼트의 수 =====");
		Double bookCnt = (Double)xpath.evaluate("count(/도서리스트/책)", xmlSrc, XPathConstants.NUMBER);
		System.out.println(bookCnt);
		
		//노드리스트,노드셋형태 반환,책 제목 모두 출력
		System.out.println("===== 모든 책 제목 =====");
		NodeList bookList = (NodeList)xpath.evaluate("/도서리스트/책/제목", xmlSrc, XPathConstants.NODESET);
		System.out.println(bookList.getLength());
		
		for (int i = 0 ; i < bookList.getLength() ; i = i + 1) {
			Element titleElement = (Element) bookList.item(i);
			System.out.println(titleElement.getTextContent());
		}
	}

}
//evaluate는 매번 사용하지만 compile 메소드를 사용하면 여러번 자료를 사용할 수 있어요

=========================================

=================================================================================

프로젝트 내 src 내 java_xml 패키지 내 ValidateApp.java

package java_xml;

import javax.xml.XMLConstants;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;

import org.xml.sax.ErrorHandler;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.SAXNotRecognizedException;
import org.xml.sax.SAXNotSupportedException;
import org.xml.sax.SAXParseException;
import org.xml.sax.XMLReader;
//error 이벤트핸들러
//xml 문서 파싱 시 밸리데이터 앱에 있는 우리가 만들려고 하는 xml 문서에 외부스키마 문서가
//있을 때 파싱할수있는 메서드를 가지고 있다면 정상적으로 만들어져있다면 result를 트루로
//만들자는 거죠.true라면 현재 xml 문서가 유효하다는 것을 판정하자는 겁니다.
//xml문서가 스키마문서가있는경우.xml문서만 바로 유효성검사하면되고
//스키마문서없으면 xsd라는 객체를 만들어서 해줘야 하고
public class ValidateApp implements ErrorHandler {
	private static boolean result;

	public void error(SAXParseException arg0) throws SAXException {
		//에러발생시 결과는 false
		ValidateApp.result = false;
	}

	public void fatalError(SAXParseException arg0) throws SAXException {
		ValidateApp.result = false;
	}

	public void warning(SAXParseException arg0) throws SAXException {

	}
	
	//스키마 문서가 지정되어 있는 경우의 유효성 검사 메소드
	public static boolean validateXml(InputSource srcXml) {
		ValidateApp.result = true;
		try {
			SAXParserFactory factory = SAXParserFactory.newInstance();
			factory.setValidating(true);
			factory.setNamespaceAware(true);
			factory.setFeature("http://apache.org/xml/features/validation/schema", true);	//스키마문서를 해석하기 위해 uri를 적어야한다 유효성 검사하기위해서

			SAXParser parser = factory.newSAXParser();	// 참조인스턴스를 parser
			XMLReader reader = parser.getXMLReader();
			//		이벤트핸들러는 에러핸들러인터페이스를 구현한 ValidateApp이라는 현클래스가 된다

			reader.setErrorHandler(new ValidateApp());
			reader.parse(srcXml);
		} catch (Exception e) {
//			파싱시 문제발생시 리절트값을 폴스로
			ValidateApp.result = false;
			// 응용프로그램에서 이 validateXml 메소드를 사용하게 될테니깐
			// return 값이 폴스로 오게 된다면 유효하지 않거나
		}
		return ValidateApp.result;
	}
	
	//스키마 문서가 지정되지 않은 문서의 유효성 검사 메소드-스키마와XML동시에확인하는메서드
	//이 메서드 작성 후 다시 ValidateXmlXsd.java 로 간다
	public static boolean validateXmlSchema(Source srcXml, Source[] srcXsd) {
		ValidateApp.result = true;
		try {
			//팩토리생성,스키마객체얻어오는과정
			SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
			Schema schema = schemaFactory.newSchema(srcXsd);
			
			Validator validator = schema.newValidator();	//schema객체를 통해 만듦
			validator.setErrorHandler(new ValidateApp());
			validator.validate(srcXml);
			//여기까지 쓰고 ValidateXmlXsd.java로 간다
		} catch (Exception e) {
			ValidateApp.result = false;
		}
		
		return ValidateApp.result;
	}
}