RDF 파일과 Jena를 사용하며 헷갈리는 부분과 Read할때 주로 사용하는 함수를 정리해보았다.
RDF용어 정리
RDF:ID = SUBJECT에서 사용되는 URI. 문서 내에서만 사용 가능하고 , 한번 밖에 사용 할 수 없음. HTML의 ID와 유사
RDF:ABOUT = 절대적인 경로. http://base.com/와 같은 기본적인 링크처럼 사용함. Html의 href와 유사
RDF:RESOURCE = Object에서 사용되는 URI. ID로 지정된 곳을 가리킨다.
RDF문서 형식
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:cim="http://iec.ch/TC57/2013/CIM-schema-cim16#"
xmlns:md="http://iec.ch/TC57/61970-552/ModelDescription/1#">
<cim:ACLineSegment rdf:ID="_c1d5c0d08f8011e08e4d00247eb1f55e">
<cim:ACLineSegment.x>6.408</cim:ACLineSegment.x>
<cim:ACLineSegment.bch>6.500000125052228E-5</cim:ACLineSegment.bch>
<cim:Equipment.aggregate>false</cim:Equipment.aggregate>
<cim:ACLineSegment.r>0.432</cim:ACLineSegment.r>
<cim:ConductingEquipment.BaseVoltage rdf:resource="#BV_400_0_NL"/>
<cim:IdentifiedObject.name>N1X3</cim:IdentifiedObject.name>
<cim:ACLineSegment.r0>0.0</cim:ACLineSegment.r0>
<cim:ACLineSegment.x0>0.0</cim:ACLineSegment.x0>
<cim:Conductor.length>35.0</cim:Conductor.length>
<cim:ACLineSegment.b0ch>0.0</cim:ACLineSegment.b0ch>
</cim:ACLineSegment>
RDF 문서 해석
* cim의 링크가 현재 http://iec.ch/TC57/2013/CIM-schema-cim16#로 지정이 되어있기 때문에, <cim:ACLineSegment>는
http://iec.ch/TC57/2013/CIM-schema-cim16#ACLineSegment로 볼 수 있다.
* <cim:ACLineSegment>의 하위태그의 <ACLineSegment.x> 와 비슷한 태그를 풀어보면
http://iec.ch/TC57/2013/CIM-schema-cim16#ACLineSegment.x의 값이 6.408이고,
http://iec.ch/TC57/2013/CIM-schema-cim16#ACLineSegment.bch의 값이 6.500000125052228E-5이라는 것, http://iec.ch/TC57/2013/CIM-schema-cim16#Equipment.aggregate의 값은 false라는 것.
여기서 값은 object에 해당한다.
* <cim:ConductingEquipment.BaseVoltage rdf:resource="#BV_400_0_NL"/> 태그는 ID가 BV_400_0_NL인 태그를 찾아서 그 태그의 하위 요소들을 Object로 삼는 것임.
Jena 코드 사용
StmtIterator iter = model.listStatements();
while(iter.hasNext()){
Statement stmt = iter.nextStatement(); // get next statement
Resource subject = stmt.getSubject(); // get the subject
Property predicate = stmt.getPredicate(); // get the predicate
RDFNode object = stmt.getObject(); // get the object
}
이터레이터를 통해서 model을 read할 때는 subject,predicate,object를 toString()이 아닌 getLocalName()으로 읽어야함.
Model 객체 Read시
Model model = ModelFactory.createDefaultModel();
FileInputStream file = new FileInputStream(new File(JenaConfig.xmlPath));
String base2 = "http://iec.ch/TC57/2013/CIM-schema-cim16";
model.read(file, base2);
Model을 읽을 땐 File 객체로 읽어야하고, base가 되는 링크 주소를 대상으로 읽어야 한다.
Jena의 RDFNode(=object) 사용
읽은 데이터의 목적어에 해당하는 Object는 Resource, Literal로 구분이 된다.
if (object.isResource()) { // object is a Resource
System.out.println(subject.getLocalName() + "," + predicate.getLocalName() + "," +
object.toString());
}
else if(object.isLiteral()) { // object is a literal
System.out.println(subject.getLocalName() + "," + predicate.getLocalName() + "," +
" \"" + object.toString() + "\"");
}
Object의 타입에 따라 다르게 출력할 수 있다.
“ \””표시는 “를 String으로 표시하겠다는 의미이다.
RDF의 타입. Jena에서의 Type 사용
String[] type = object.toString().split("#");
RDF 에는 Type이 명시되어있는데 <cim:ACLineSegment> 태그를 예로 들자면 Type은 ACLineSegment이다.
이 Type을 꺼내서 사용하려면 Property에 Type이 있는지 확인해야한다.
If(property.getLocalName().equals(“type”){}
이 코드는 “문자가 일치하면”이라는 코드인데,
앞에서 얘기한 Type은 predicate에 속해있기 때문에 predicate(행동)으로 조건을 붙이면 한 태그에 type의 값(RDFNode)를 알 수 있게 된다.
즉, Subject: ACLineSegment의 predicate: type은 object: ACLineSegment이다.
* Jena는 StmtIterator iter = model.listStatements();를 사용해서 처음부터 끝까지 단순하게 조회할 수 있기도 하지만 Jena에서는 특정 Subject의 ID와 Property, Type별로 조회를 할 수 있게 하는 함수를 제공하여 조회 성능을 좀 더 높이고 코드를 간결하게 작성할 수 있게끔 해준다.
Jena에서 주로 사용되는 함수
리턴값 |
함수명 |
인자 |
출력값 |
Resource(링크) |
RDFNode.asResource |
() |
링크#value |
Set<>맵 |
Model.getNsPrefixMap |
() |
네임스페이스 링크리스트 반환 |
Property |
Model.createProperty |
Property |
Property를 생성 |
NodeIterator |
Model.listObjectsOfProperty |
Property |
Property와 일치하는 RDFNode의 NodeIterator 반환 |
ResIterator |
Model.listResourcesWithProperty |
Property |
Property와 일치하는 Resource의 ResIterator 반환 |
Statement |
Model.getProperty |
Resource,Property |
Resource, Property와 일치하는 Statement 반환 |
Resource |
Statement.getResource |
() |
Terminal |
Resource |
Statement.getSubject |
() |
_c1d5c0e88f8011e08e4d00247eb1f55e_T1와 같은 고유ID값 |
Property |
Statement.getPredicate |
() |
Type와 같은 핸들값 |
RDFNode |
Statement.getObject |
() |
Jena를 사용한 예시 코드
private static Map<String, Property> typeMap = new HashMap<>();
private static Map<String, TreeItem> typeTreeMap = new HashMap<>();
// 네임 스페이스에 지정된 RDF 링크 리스트
Iterator<String> it = model.getNsPrefixMap().keySet().iterator();
while (it.hasNext()) {
String nameRDF = model.getNsPrefixMap().get(it.next());
Property property = model.createProperty(nameRDF + "type"); // 링크 + type이라는 이름의 프로퍼티를 생성
NodeIterator nodeIterator = model.listObjectsOfProperty(property); // type과 일치하는 property리스트를 가져옴.
while (nodeIterator.hasNext()) {
RDFNode object = nodeIterator.next();
String[] tempString = object.toString().split("#");
String type = tempString[1]; // type을 임시로 초기화
// http://iec.ch/TC57/2013/CIM-schema-cim16#Terminal 에서 Terminal만 가져온 것.
// Type 트리 아이템 생성(SWT용)
TreeItem item = new TreeItem(tree, SWT.MULTI);
item.setText(type); // Type Tree 초기화
typeMap.put(type, property); // property용 맵 초기화
typeTreeMap.put(type, item); // 트리 맵 초기화
}
}
Iterator<String> typeIterator = typeMap.keySet().iterator(); // type맵 기준의 이터레이터 생성
while (typeIterator.hasNext()) {
String key = typeIterator.next(); // type 요소 가져옴.
Property typeProperty = typeMap.get(key); // type-property를 가져옴
ResIterator resIterator = model.listResourcesWithProperty(typeProperty); // 가져온 property와 일치하는 해당 resource 이터레이터를 가져옴.
while(resIterator.hasNext()) {
Resource subject = resIterator.next();
Statement stmt = model.getProperty(subject, typeProperty); // resource와 property와 일치하는 statement요소 가져옴
if(!key.equals(stmt.getResource().getLocalName())) { // type요소와 resource요소가 일치하지 않으면 다음 검색
continue;
}
// type요소와 resource요소가 일치할 경우
// subject(=id) 트리 아이템 생성
TreeItem subjectItem = new TreeItem(typeTreeMap.get(key), SWT.MULTI);
subjectItem.setText(subject.getLocalName());
// resource 하위 태그의 statement 이터레이터를 가져옴
StmtIterator stmtIterator = subject.listProperties();
while (stmtIterator.hasNext()) {
Statement statement = stmtIterator.next();
// object 트리 아이템 생성
TreeItem objectItem = new TreeItem(subjectItem, SWT.MULTI);
objectItem.setText(
statement.getPredicate().getLocalName() + " : " + statement.getObject().toString());
}
}
}
Iterator와 위에서 언급한 Resource, Property를 사용해서 특정 태그를 검색하는 기능을 활용해서
코드의 일부일 뿐이니 listResourceWithProperty나 getResource나 특정 태그들이 사용된 부분과 주석을 확인해서 활용하면 되겠다.
코드의 진행 흐름을 간략히 설명하자면,
1) RDF 문서의 Type(Property)을 검색한다. => Type 트리(1단계)에 담기
2) Type 별로 Map에 담는다.
3) TypeMap을 기반으로 Type(Property)과 일치하는 Resource(subject) 이터레이터를 가져온다.
4) Resource(=id)를 트리(2단계)에 담기
5) Resource.listProperties()로 Resource의 하위태그를 몽땅 가져온다.
6) object를 트리(3단계)에 담기
RDF문서를 Tree 형태로 나타낸 코드이다.
출력 결과
'Java > RDF&Jena' 카테고리의 다른 글
Jena SPARQL 사용 방법 및 소스 코드 (0) | 2020.03.20 |
---|---|
Jena SDB 사용 예제 (0) | 2020.03.20 |
RDF - Eclipse Apache Jena Xml 파일 읽어오기 (0) | 2020.03.10 |
RDF - Eclipse Apache Jena 다운로드 및 사용법 (0) | 2020.03.04 |
RDF(Resource Description Framework)란 (0) | 2020.03.04 |