2016-10-20 14 views
2

XPath 표현식을 사용하여 XML 파일에서 프로그램을 읽을 수 있어야합니다. 이미 JDOM2를 사용하여 프로젝트를 시작했지만 다른 API로 전환하는 것은 바람직하지 않습니다. 어려움은 프로그램이 요소 나 속성을 읽어야하는지 미리 알지 못한다는 것입니다. API는 XPath 표현식을 제공하여 콘텐츠 (문자열)를 수신하는 기능을 제공합니까? JDOM2의 XPath에 대해 알고있는 것부터, 다양한 유형의 객체를 사용하여 속성 또는 요소를 가리키는 XPath 표현식을 평가합니다. XPath 표현식이 가리키는 속성/요소의 내용에만 관심이 있습니다. 또 다른 생각이 XPath 식의 '@'문자를 검색하는 것입니다Java XML JDOM2 XPath - XPath 표현식을 사용하여 XML 속성 및 요소에서 텍스트 값을 읽습니다.

package exampleprojectgroup; 

import java.io.IOException; 
import java.util.LinkedList; 
import java.util.List; 
import org.jdom2.Attribute; 
import org.jdom2.Document; 
import org.jdom2.Element; 
import org.jdom2.JDOMException; 
import org.jdom2.filter.Filters; 
import org.jdom2.input.SAXBuilder; 
import org.jdom2.input.sax.XMLReaders; 
import org.jdom2.xpath.XPathExpression; 
import org.jdom2.xpath.XPathFactory; 


public class ElementAttribute2String 
{ 
    ElementAttribute2String() 
    { 
     run(); 
    } 

    public void run() 
    { 
     final String PATH_TO_FILE = "c:\\readme.xml"; 
     /* It is essential that the program has to work with a variable amount of XPath expressions. */ 
     LinkedList<String> xPathExpressions = new LinkedList<>(); 
     /* Simulate user input. 
     * First XPath expression points to attribute, 
     * second one points to element. 
     * Many more expressions follow in a real situation. 
     */ 
     xPathExpressions.add("/bookstore/book/@category"); 
     xPathExpressions.add("/bookstore/book/price"); 

     /* One list should be sufficient to store the result. */ 
     List<Element> elementsResult = null; 
     List<Attribute> attributesResult = null; 
     List<Object> objectsResult = null; 
     try 
     { 
      SAXBuilder saxBuilder = new SAXBuilder(XMLReaders.NONVALIDATING); 
      Document document = saxBuilder.build(PATH_TO_FILE); 
      XPathFactory xPathFactory = XPathFactory.instance(); 
      int i = 0; 
      for (String string : xPathExpressions) 
      { 
       /* Works only for elements, uncomment to give it a try. */ 
//    XPathExpression<Element> xPathToElement = xPathFactory.compile(xPathExpressions.get(i), Filters.element()); 
//    elementsResult = xPathToElement.evaluate(document); 
//    for (Element element : elementsResult) 
//    { 
//     System.out.println("Content of " + string + ": " + element.getText()); 
//    } 

       /* Works only for attributes, uncomment to give it a try. */ 
//    XPathExpression<Attribute> xPathToAttribute = xPathFactory.compile(xPathExpressions.get(i), Filters.attribute()); 
//    attributesResult = xPathToAttribute.evaluate(document); 
//    for (Attribute attribute : attributesResult) 
//    { 
//     System.out.println("Content of " + string + ": " + attribute.getValue()); 
//    } 

       /* I want to receive the content of the XPath expression as a string 
       * without having to know if it is an attribute or element beforehand. 
       */ 
       XPathExpression<Object> xPathExpression = xPathFactory.compile(xPathExpressions.get(i)); 
       objectsResult = xPathExpression.evaluate(document); 
       for (Object object : objectsResult) 
       { 
        if (object instanceof Attribute) 
        { 
         System.out.println("Content of " + string + ": " + ((Attribute)object).getValue()); 
        } 
        else if (object instanceof Element) 
        { 
         System.out.println("Content of " + string + ": " + ((Element)object).getText()); 
        } 
       } 
       i++; 
      } 
     } 
     catch (IOException ioException) 
     { 
      ioException.printStackTrace(); 
     } 
     catch (JDOMException jdomException) 
     { 
      jdomException.printStackTrace(); 
     } 
    } 
} 

에, :

<?xml version="1.0" encoding="UTF-8"?> 
<bookstore> 
    <book category="COOKING"> 
    <title lang="en">Everyday Italian</title> 
    <author>Giada De Laurentiis</author> 
    <year>2005</year> 
    <price>30.00</price> 
    </book> 
    <book category="CHILDREN"> 
    <title lang="en">Harry Potter</title> 
    <author>J K. Rowling</author> 
    <year>2005</year> 
    <price>29.99</price> 
    </book> 
    <book category="WEB"> 
    <title lang="en">XQuery Kick Start</title> 
    <author>James McGovern</author> 
    <author>Per Bothner</author> 
    <author>Kurt Cagle</author> 
    <author>James Linn</author> 
    <author>Vaidyanathan Nagarajan</author> 
    <year>2003</year> 
    <price>49.99</price> 
    </book> 
    <book category="WEB"> 
    <title lang="en">Learning XML</title> 
    <author>Erik T. Ray</author> 
    <year>2003</year> 
    <price>39.95</price> 
    </book> 
</bookstore> 

이 같이 내 프로그램 모습입니다 : 여기

는 예를 들어, XML 파일입니다 속성 또는 요소를 가리키는 지 판별하십시오. 더 나은 해결책이 있었으면 좋겠지 만 이것은 원하는 결과를 제공합니다. JDOM2 API는이 문제에 유용한 것을 제공합니까? 내 요구 사항을 충족시키기 위해 코드를 재 설계 할 수 있습니까?

미리 감사드립니다.

답변

0

XPath 표현식은 표현식에있는 XPath 함수/값의 반환 유형에 민감한 시스템에서 컴파일해야하기 때문에 입력/전송하기가 어렵습니다. JDOM은 제 3 자 코드를 사용하여이를 수행하며 제 3 자 코드에는 JDOM 코드의 컴파일 시간에 해당 유형을 상관시키기위한 메커니즘이 없습니다. XPath 표현식은 String, boolean, Number 및 Node-List와 유사한 내용을 포함하여 다양한 유형의 내용을 반환 할 수 있습니다.

대부분의 경우식이 계산되기 전에 XPath 식의 반환 형식이 알려져 있고 프로그래머에게는 결과를 처리하기위한 "올바른"캐스팅/기대 값이 있습니다.

귀하의 경우, 귀하는 그렇지 않으며 표현식이보다 동적입니다.

난 당신이 콘텐츠를 처리하는 도우미 함수를 선언하는 것이 좋습니다

private static final Function extractValue(Object source) { 
    if (source instanceof Attribute) { 
     return ((Attribute)source).getValue(); 
    } 
    if (source instanceof Content) { 
     return ((Content)source).getValue(); 
    } 
    return String.valueOf(source); 
} 

을이 적어도 당신의 코드를 neaten 것입니다, 당신은 Java8 스트림을 사용하는 경우, 매우 컴팩트 될 수 있습니다

List<String> values = xPathExpression.evaluate(document) 
         .stream() 
         .map(o -> extractValue(o)) 
         .collect(Collectors.toList()); 

요소 노드에 대한 XPath 사양은 string-value이 요소의 text() 콘텐츠와 모든 자식 요소의 콘텐츠의 연결임을 나타냅니다. 따라서, 다음 XML 조각에서 :

<a>bilbo <b>samwise</b> frodo</a> 

a 요소에 getValue()bilbo samwise frodo를 반환하지만, getText()bilbo frodo를 반환합니다. 값 추출을 위해 사용할 메커니즘을 신중하게 선택하십시오.

+0

JDOM2의'Attribute'는'Content'의 서브 클래스입니까? http://www.jdom.org/docs/apidocs/org/jdom2/Attribute.html에 나와 있지 않아서 왜 대답이 'XPathExpression xPathExpression = xPathFactory.compile (xPathExpressions.get (i)), Filters.content())'는 요소와 속성을 처리합니다. –

+0

아 ... 허튼 소리. 나는 Attributes가 만족스럽지 않다는 것을 잊었다. 그것은'getValue()'메소드를 가지고 있으며 나는 가정했다. 이것에 대해 잠시 생각해 봅시다. – rolfl

+0

나는 그것을 조사하는 것 이외의 모호한 XPath 결과를 처리하는 더 좋은 방법을 생각할 수 없다. Element 노드와 Attribute 노드가 공통 조상을 공유하는 경우 JDOM이 작업을 좀 더 쉽게 만들 수 있지만 가능하지 않은 다른 이유가 있습니다. 나는 대답을 편집하여 OP로 설명 된 기본 메커니즘을 변경하는 대신 코드를 정리하는 기능 추출을 권장합니다. – rolfl

0

필자는 똑같은 문제점을 안고 Xpath의 초점이 속성인지를 인식하는 방법을 택했습니다. 나는 두 가지 기능으로 해결했다.먼저 나중에 사용할 수 있도록하여 XPathExpression 준수 :

XPathExpression xpExpression; 
    if (xpath.matches( ".*/@[\\w]++$")) { 
     // must be an attribute value we're after.. 
     xpExpression = xpfac.compile(xpath, Filters.attribute(), null, myNSpace); 
    } else { 
     xpExpression = xpfac.compile(xpath, Filters.element(), null, myNSpace); 
    } 

두 번째 평가합니다 및 값을 반환

Object target = xpExpression.evaluateFirst(baseEl); 
if (target != null) { 
    String value = null; 
    if (target instanceof Element) { 
     Element targetEl = (Element) target; 
     value = targetEl.getTextNormalize(); 
    } else if (target instanceof Attribute) { 
     Attribute targetAt = (Attribute) target; 
     value = targetAt.getValue(); 
    } 

내가 그 도우미 기능은 이전의 대답에 제안 당신이 선호 여부 코딩 스타일의 문제가 의심을 또는이 방법. 둘 중 하나가 작동합니다.