2009-05-27 10 views
12

Java'da ad alanları içeren bir xpath çözümlenmesi, NamespaceContext nesnesinin kullanılması, ad öneklerini eşleme URL'leri için eşleme gerektirir ve bunun tersi de geçerlidir. Ancak, kendim uygulamaktan başka bir NamespaceContext almak için hiçbir mekanizma bulamıyorum. Bu karşı sezgisel görünüyor.Ad alanıBaşlığı ve XPath ile ad alanları kullanma

soru: bir belgeden bir NamespaceContext elde etmek ya da birini ya da tamamen önekleri bırakmak ve tam nitelikli adlarla xpath belirtmek için bu olmazsa oluşturmak için herhangi bir kolay yolu var mı?

+1

Sorunuzdaki bazı basit yanlışlıklar var: Xath ile tüm ad alanlarını XPath ile alabilirsiniz (bunun için SO yanıtları vardır), ancak basit bir önek ad alanı URI'sini sınıflandıramazsınız çünkü * * Her düğüm için ** farklı bir sınır olabilir. Düğümleri seçerken, önceden istediğiniz elemanların isimlerini bilmelisiniz ** ve bunların isimlerini içeren URI ** –

cevap

11

Kendi sınıfınızı yazmadan bir NamespaceContext örneğini almak mümkündür. class-use sayfasında, javax.xml.stream paketini kullanarak bir tane alabilirsiniz.

String ctxtTemplate = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" />"; 
NamespaceContext nsContext = null; 

XMLInputFactory factory = XMLInputFactory.newInstance(); 
XMLEventReader evtReader = factory 
    .createXMLEventReader(new StringReader(ctxtTemplate)); 
while (evtReader.hasNext()) { 
    XMLEvent event = evtReader.nextEvent(); 
    if (event.isStartElement()) { 
    nsContext = ((StartElement) event) 
     .getNamespaceContext(); 
    break; 
    } 
} 

System.out.println(nsContext.getNamespaceURI("")); 
System.out.println(nsContext.getNamespaceURI("foo")); 
System.out.println(nsContext 
    .getNamespaceURI(XMLConstants.XMLNS_ATTRIBUTE)); 
System.out.println(nsContext 
    .getNamespaceURI(XMLConstants.XML_NS_PREFIX)); 

forgoing önekleri tamamen belirsiz ifadeler yol açması muhtemeldir - Eğer ad önekleri düşmesi istiyorsanız, belge biçimini değiştirmek gerekiyordu. Bir belgeden bir bağlam oluşturmak, mutlaka mantıklı değildir. önekler bu kodda olduğu gibi, XPath ifadesinde kullanılan olanlar, herhangi bir belgede olanları değil eşleşmesi gerekir:

String xml = "<data xmlns=\"http://base\" xmlns:foo=\"http://foo\" >" 
    + "<foo:value>" 
    + "hello" 
    + "</foo:value>" 
    + "</data>"; 
String expression = "/stack:data/overflow:value"; 
class BaseFooContext implements NamespaceContext { 
    @Override 
    public String getNamespaceURI(String prefix) { 
    if ("stack".equals(prefix)) 
     return "http://base"; 
    if ("overflow".equals(prefix)) 
     return "http://foo"; 
    throw new IllegalArgumentException(prefix); 
    } 

    @Override 
    public String getPrefix(String namespaceURI) { 
    throw new UnsupportedOperationException(); 
    } 

    @Override 
    public Iterator<String> getPrefixes(
     String namespaceURI) { 
    throw new UnsupportedOperationException(); 
    } 
} 
XPathFactory factory = XPathFactory.newInstance(); 
XPath xpath = factory.newXPath(); 
xpath.setNamespaceContext(new BaseFooContext()); 
String value = xpath.evaluate(expression, 
    new InputSource(new StringReader(xml))); 
System.out.println(value); 

Ne STAX API ne de yukarıdaki biri tarafından döndürülen uygulama tam class/method uygulamak dokümanda tanımlanan sözleşmeler. Tam, harita tabanlı bir uygulama here alabilirsiniz.

+0

Bu tür korkularımı doğrular. Sınıfı kendim uygulamadıkça keyfi haritalama yapan bir NamespaceContext'im olamaz. Ancak, XML akışları örneği, en azından ilk örnekte yaptığınız gibi bir mikro belge oluşturan bir statik işlevden bir NamespaceContext oluşturmak için bir yol verir. Bunu deneyeceğim. – Jherico

+0

Evet, varsayılan bir uygulamanın olmadığı bir acıdır. – McDowell

+0

Bunun aptalca olduğunu itiraf ediyorum. Kamu API'larında kullanılan diğer java arayüzlerinin kamuya açık beton uygulamaları veya fabrikaları bulunmamaktadır? – Jherico

7

Sadece xpath ve NamespaceContexts kullanarak çalışıyorum. on developerworks numaralı sorunun iyi bir tedavisiyle karşılaştım.

+2

IBM'den bu denemeyi okuduktan sonra, neden varsayılan bir uygulama olmadığını anlayabiliyorum. Bu sorunu çözmek için çeşitli yollar vardır. Dedi ki: Apache gibi bir açık kaynak grubu neden farklı uygulamalardan oluşan bir kütüphane yaratmıyor? – kevinarpe

6

"Apache WebServices Common Utilities" adında NamespaceContextImpl adlı uygun bir uygulama buldum.

Bu sınıfa almak için aşağıdaki maven bağımlılığını kullanabilirsiniz:

<dependency> 
    <groupId>org.apache.ws.commons</groupId> 
    <artifactId>ws-commons-util</artifactId> 
    <version>1.0.1</version> 
</dependency> 

aşağıdaki şekilde kullanmak ettik (biliyorum onun saksofon için inşa fakat kod, onun ok okuduktan sonra):

NamespaceContextImpl nsContext = new NamespaceContextImpl(); 
nsContext.startPrefixMapping("foo", "my.name.space.com"); 

EndPrefixMapping çağrılmanıza gerek yoktur.

+1

hala ad alanlarını sabitliyorsunuz! – Meitham

+0

Tam olarak aradığım şey. Javadoc: http://ws.apache.org/commons/util/apidocs/index.html –

+0

Meitham - İsim alanını kodlamaktan kaçınan hiçbir şekilde değilim. Bu ad alanı bağlamını oluşturarak XPath aramasını önyükleme yapıyorsunuz, her bir ad alanına kısa bir önek eşleştirerek başlatacaksınız * bu belgede bulunan * bilirsiniz * ve XPath ile arama yaparken yolu tanımlarken bu önekleri kullanırsınız. –

1

Eğer onların NamespaceContext uygulama org.springframework.util.xml.SimpleNamespaceContext

Bu Asaf Mesika gelen biri gibi benzer bir cevaptır yeniden kullanabilirsiniz Bahar çerçevesini kullanıyorsanız. Bu nedenle, belgenize dayalı otomatik bir NamespaceContext vermez. Kendin inşa etmelisin. Yine de size yardımcı olur, çünkü en azından başlangıç ​​için bir uygulama sunar.

Benzer bir sorunla karşılaştığımızda, hem SimpleNamespaceContext hem de "Apache WebServices Common Utilities" yayları çalıştı. Apache WebServices Common Utilities ürününe eklenen jar bağımlılığından kaçınmak istedik ve Spring'i kullandık, çünkü uygulamamız Spring tabanlı.

0

Eğer Jersey 2 kullanarak ve yalnızca bir varsayılan XML ad alanını (xmlns="...") sahip iseniz, SimpleNamespaceResolver kullanabilirsiniz: İsterseniz elle de xmlns belirtebilirsiniz

<?xml version="1.0" encoding="UTF-8"?> 
<Outer xmlns="http://host/namespace"> 
    <Inner /> 
</Outer> 
DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance(); 
docBuilderFactory.setNamespaceAware(true); 
DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); 

Document document = docBuilder.parse(new File("document.xml")); 
String query = "/t:Outer/t:Inner"; 

XPath xpath = XPathFactory.newInstance().newXPath(); 
String xmlns = document.getDocumentElement().getAttribute("xmlns"); 
xpath.setNamespaceContext(new SimpleNamespaceResolver("t", xmlns)); 

NodeList nodeList = (NodeList) xpath.evaluate(query, document, XPathConstants.NODESET); 

//nodeList will contain the <Inner> element 

.