Jena RDF api 활용하기 (2)

     

저번 포스팅에 이어 연속해서 Jena RDF API를 사용해 보도록 하겠습니다.


지난번에 만들었던 간단한 모델에다가 추가로 여러가지를 덧붙입니다.


리소스는 속성을 갖고 속성을 값을 가질 수 있다고 했습니다. 하지만 리소스가 또 다른 리소스를 속성으로 가질수 있습니다. 다음과 같은 모델을 만들어 봅니다.




위에 모델에서 JohnSmith라는 URI는 vacard를 두개를 가지고 있는데 하나는 FN이고 하나는 N입니다. vcard:N이 가지고 있는것은 값이 아니라 리소스이고 이 리소스는 두개의 속성을 가지고 있습니다. 하나는 vcard:Given이라는 이름이고 하나는 vcard:Family 라는 성입니다. 즉 존스미스 리소스는 두개의 속성이 있는데 하나는 풀 네임을 값으로 갖는 속성이고 하나는 리소스인데 이 리소스는 성과 이름을 분리하여 속성값으로 가지고 있는 리소스가 됩니다. 아직까지는 코드 구현이 아주 간단합니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.vocabulary.VCARD;
 
 
public class Main {
    
    // 사용되는 문자열 정의
    static String personURI    = "http://somewhere/JohnSmith";
    static String fullName     = "John Smith";
    static String givenName = "Smith";
    static String familyName = "John";
    
    public static void main(String[] args){
        
 
        // 빈 모델 생성
        Model model = ModelFactory.createDefaultModel();
 
        // 리소스 생성 및 속성 추가
        Resource johnSmith = model.createResource(personURI)
                .addProperty(VCARD.FN, fullName)
                .addProperty(VCARD.N, model.createResource()
                        .addProperty(VCARD.Given, givenName)
                        .addProperty(VCARD.Family, familyName));
 
         // 출력
         model.write(System.out, "RDF/XML");
    }
 
}
 


위와같은 코드를 입력하면 됩니다. 지난 포스팅의 코드와 별로 차이가 없는 것을 알 수 있습니다. 단지 속성을 추가하기위해 addProperty를 할때 속성중 하나는 리소스이므로 리소스를 하나 더 만드는 과정이 추가되어 있습니다. 위 코드를 실행시키면 아래와 같은 결과가 나오게 됩니다.


1
2
3
4
5
6
7
8
9
10
11
12
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:vcard="http://www.w3.org/2001/vcard-rdf/3.0#" > 
  <rdf:Description rdf:nodeID="A0">
    <vcard:Family>John</vcard:Family>
    <vcard:Given>Smith</vcard:Given>
  </rdf:Description>
  <rdf:Description rdf:about="http://somewhere/JohnSmith">
    <vcard:N rdf:nodeID="A0"/>
    <vcard:FN>John Smith</vcard:FN>
  </rdf:Description>
</rdf:RDF>


우리가 만든 모델이 RDF언어로 나오게 됩니다. 짠~ 아직까지는 만드는 부분이고 활용하는 것은 안나오네요. 여기까지가 홈페이지에서 tutorial 2까지입니다.


다음설명은 Statements 입니다.

RDF모델에서 화살표를 statement라고 표현한다고 하네요. 구지 번역하면 '상태'정도로 말할 수 있겠습니다. 이 상태라는 것은 리소스에대한 사실을 표현하게 되는데 3가지 파트로 나뉘어 있습니다.


subject : 화살표가 나오는 지점의 리소스

predicate : 화살표의 이름(속성)

object : 화살표가 가리키는 리소스나 문자값


이런 3가지 파트를 가지고 있기때문에 상태를 다른말로 '트리플'이라고도 표현한다고 합니다. RDF 모델은 이런 상태들의 집합으로 이루어진 모델입니다.


자바에서는 iterator라는 기능이 있는데 반복하는 기능입니다. 문자열이나 스트림을 사용할 경우 다음 문자열이나 스트림으로 넘어가기위해 Next() 명령어를 자주 사용하는데 이런것을 iterator라고 합니다. jena 에서도 iterator를 사용해서 상태값을 이동하는 메소드가 있다고 합니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.vocabulary.VCARD;
 
 
public class Main {
    
    // 사용되는 문자열 정의
    static String personURI    = "http://somewhere/JohnSmith";
    
    static String givenName = "Smith";
    static String familyName = "John";
    static String fullName     =  familyName + " " + givenName;
    public static void main(String[] args){
        
 
        // 빈 모델 생성
        Model model = ModelFactory.createDefaultModel();
    
        
        // 리소스 생성 및 속성 추가
        Resource johnSmith = model.createResource(personURI)
                .addProperty(VCARD.FN, fullName)
                .addProperty(VCARD.N, model.createResource()
                        .addProperty(VCARD.Given, givenName)
                        .addProperty(VCARD.Family, familyName));
 
        //Iterator 선언
        StmtIterator iter = model.listStatements();
         
        // 출력
         while (iter.hasNext()) {
                Statement stmt      = iter.nextStatement();  // 다음 상태를 읽는다.
                Resource  subject   = stmt.getSubject();     // subject를 읽는다.
                Property  predicate = stmt.getPredicate();   // predicate를 읽는다.
                RDFNode   object    = stmt.getObject();      // object를 읽는다.
 
                System.out.print(subject.toString());
                System.out.print(" " + predicate.toString() + " ");
                if (object instanceof Resource) {
                   System.out.print(object.toString());
                } else {
                    // object is a literal
                    System.out.print(" \"" + object.toString() + "\"");
                }
 
                System.out.println(" .");
            } 
    }
 
}


위에 코드는 존스미스 모델에 Iterator 기능을 추가한 것입니다. 이전 코드에서는 출력을 위해 modle.write()메소드를 사용했지만 이번에는 iterator기능을 이용해서 출력을 해보았습니다. 

While문을 보시면 iterator가 다음값을 가질경우에만 실행이 되므로 처음부터 끝까지 모든 속성값들을 반환한다고 보시면 됩니다. 


위 코드를 실행하면 다음과 같은 결과값이 출력됩니다.


1
2
3
4
http://somewhere/JohnSmith http://www.w3.org/2001/vcard-rdf/3.0#N 3498f6a9:143fd41bbc8:-7fff .
http://somewhere/JohnSmith http://www.w3.org/2001/vcard-rdf/3.0#FN  "John Smith" .
3498f6a9:143fd41bbc8:-7fff http://www.w3.org/2001/vcard-rdf/3.0#Family  "John" .
3498f6a9:143fd41bbc8:-7fff http://www.w3.org/2001/vcard-rdf/3.0#Given  "Smith" .


결과값을 자세히보면 3개의 영역으로 이루어 진것을 볼 수 있습니다. 띄어쓰기를 기준으로 첫번째 문자열은 subject를 가리키고 가운데는 predicate, 마지막은 object를 출력합니다. 존스미스모델에는 총 4개의 속성이 있고 각 속성이 가지고있는 3가지 값들을 알 수 있습니다. 여기서 3498f6a9.... 라는 16진수값들을 볼 수있는데 이것은 jena에서 사용하는 고유한 식별자라고 합니다. URI와는 다른 것이니 혼동하지 않도록 합니다. 이 고유한 식별자는 N-Triple이라고 불리고 "triple notation"이라는 의미로 사용된다고 합니다. 이것에 의미는 다음챕터에서 알려주겠다고 합니다. 그럼.. 다음에 알기로 하고...


다음은 RDF 파일을 출력법입니다.

저번 포스팅에서 언급했던것 같은데 model.write()메소드를 사용해서 파일로 코딩으로 만든 RDF파일을 저장 할 수 있습니다. 물론 system.out 을 통해 콘솔창으로도 결과값을 출력 할 수 있죠. 이부분이 이제서야 나오네요. (전시간부터 저는 쓰고 있었지만...)


맨 처음 모델을 출력했을때 다음과 같은 RDF 언어가 나왔습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
<rdf:RDF
  xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'
  xmlns:vcard='http://www.w3.org/2001/vcard-rdf/3.0#'
 >
  <rdf:Description rdf:about='http://somewhere/JohnSmith'>
    <vcard:FN>John Smith</vcard:FN>
    <vcard:N rdf:nodeID="A0"/>
  </rdf:Description>
  <rdf:Description rdf:nodeID="A0">
    <vcard:Given>John</vcard:Given>
    <vcard:Family>Smith</vcard:Family>
  </rdf:Description>
</rdf:RDF>


일단 처음시작은 rdf:RDF로 시작합니다. 이건 다릏게 표현해도 된다는데 이렇게 쓰는게 일반적이라고 하더군요. 이는 xml언어로 표현된 rdf이기때문에 이문서가 RDF문서라는 것을 말해주는 역할입니다. 그아래는 네임스페이스에 대한 정의가 있는데 rdf와 vcard라는 네임스페이스에 대한 정의를 하고 있습니다. 이 네임스페이스는 다음과 같은 URI를 참고한다~ 라는 역할입니다.


rdf라는 네임스페이스를 쓴건 두군데입니다. 첫번쨰로 rdf:Description이 있는데 이것은 리소스의 URI를 정의할때 사용합니다. rdf:about속성으로 URI를 표현하고 이 것이 없으면 비어있는 노드가 됩니다.


빈노드라... 그렇습니다. 위에 모델을 보면 johnsmith리소스가 갖는 속성들중 하나는 빈 리소스입니다. 이것을 표현할때 "RDF/XML"형식으로 쓰게되면 형식에 맞지않는 RDF문서가 만들어 진다고합니다. 왜 이걸 표시하고 나서 알려주나구요 ... 아무튼 이럴때는 "RDF/XML-ABBREV"형태로 바꾸어서 출력합니다. 이러면 위에 결과와 조금 다른 결과를 보실 수 있습니다.


1
2
3
4
5
6
7
8
9
10
11
12
<rdf:RDF
    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
    xmlns:vcard="http://www.w3.org/2001/vcard-rdf/3.0#">
  <rdf:Description rdf:about="http://somewhere/JohnSmith">
    <vcard:N rdf:parseType="Resource">
      <vcard:Family>John</vcard:Family>
      <vcard:Given>Smith</vcard:Given>
    </vcard:N>
    <vcard:FN>John Smith</vcard:FN>
  </rdf:Description>
</rdf:RDF>
 


위의 결과와 다른게 보이시나요? 위에서는 vcard:N에대한 표현을 nodeID=A0라고 표현했지만 아래에서는 rdf:parseType="Resource"라고 표현했습니다. 아마 위에 코드에서는 리소스가 이름이 없기때문에 단순한 노드라고 생각했고 아래에서는 비어있지만 리소스의 역할을 하기때문에 "Resource"라고 표기한 것 같습니다. 사용할때 둘의 차이는 나중에 가봐야 알겠지만요 ^^;;


하지만 "RDF/XML-ABBREV"는 긴 문서에는 적합하지 않다고 하네요. 긴 문서인데 이름없는 리소스가 있을경우 "N-TRIPLE"형태로 사용하라고 나옵니다. 이 것을 사용했을 경우에는 위에서 했던 것과 비슷한 결과를 얻을 수 있습니다. (iterator 사용했던 코드처럼..)



다음은 RDF파일을 읽어오는 코드입니다. 파일로되어있는 RDF문서를 읽어와서 수정하거나 새로 쓰는 것이지요. 가장 필요한 기능이 아닐까 싶습니다. 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.util.FileManager;
 
import java.io.*;
 
 
public class Main extends Object {
 
                              
    public static void main (String args[]) {
 
        Model model = ModelFactory.createDefaultModel();
 
        InputStream in = FileManager.get().open( "file:C:/Users/Cho/Documents/owl/test.owl");
        if (in == null) {
            throw new IllegalArgumentException( "File not found");
        }
        
     
        model.read(in, "");
                    
     
        model.write(System.out, "RDF/XML");            
    }
}
 


위에 코드를 보면 내 컴퓨터에 있는 owl 문서를 읽어서 출력하는 과정입니다. 여기서는 InputStream을 사용해서 일단 파일을 읽어온다음 model.read를 사용해서 스트림을 읽어오는 방식을 사용했는데 이렇게 하지 않고 그냥 model.read("파일주소") 메소드를 사용하면 스트림 사용없이 바로 읽어 올 수 있습니다. 하지만 이경우 아마 try catch문을 사용했던것 같네요. 안정성면에서 스트림을 사용하는것이 좋아 보입니다.


홈페이지에 공식 설명서에서는 제공하는 rdf문서를 출력하라고 했는데 제가 다운받은 폴더를 뒤져보았으나 그런게 없었으므로 저는 그냥 임의의 owl파일을 읽어왔습니다. 출력화면은 너무 길어서 못가져오겠고... 위에 결과들과 비슷하게 나오게 됩니다 ^^;; 


오늘 포스팅은 여기까지!!

반응형

'Study > OWL,RDF' 카테고리의 다른 글

Jena Ontology API  (0) 2014.02.10
Jena RDF api 활용하기 (3)  (0) 2014.02.05
Jena RDF api 활용하기  (0) 2014.02.04
Jena API  (0) 2014.02.03
Ontology(온톨로지)란? (2)  (3) 2014.02.03

댓글

Designed by JB FACTORY