it-swarm.dev

Jak dotazovat XML pomocí jmenných prostorů v jazyce Java s XPath?

Když můj XML vypadá takto (ne xmlns), můžu se na něj snadno zeptat pomocí XPath jako /workbook/sheets/sheet[1]

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook>
  <sheets>
    <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
  </sheets>
</workbook>

Ale když to tak vypadá, tak nemůžu

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">
  <sheets>
    <sheet name="Sheet1" sheetId="1" r:id="rId1"/>
  </sheets>
</workbook>

Nějaké nápady?

60
Inez

Váš problém je výchozí obor názvů. Podívejte se na tento článek, jak se vypořádat s jmennými prostory ve vašem XPath: http://www.edankert.com/defaultnamespaces.html

Jedním z těchto závěrů je:

Abychom tedy mohli používat výrazy XPath Na obsah XML definovaný v (Výchozí) oboru názvů, musíme Specifikovat mapování předpon jmenného prostoru

Všimněte si, že to neznamená, že musíte změnit svůj zdrojový dokument jakýmkoliv způsobem (i když můžete, pokud si to přejete, umístit předpony jmenného prostoru). Zní to zvláštní, že? Co jste will uděláte, vytvořte mapování předpony jmenného prostoru ve vašem kódu Java a použijte uvedený prefix ve vašem výrazu XPath. Zde vytvoříme mapování z spreadsheet do výchozího jmenného prostoru.

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();

// there's no default implementation for NamespaceContext...seems kind of silly, no?
xpath.setNamespaceContext(new NamespaceContext() {
    public String getNamespaceURI(String prefix) {
        if (prefix == null) throw new NullPointerException("Null prefix");
        else if ("spreadsheet".equals(prefix)) return "http://schemas.openxmlformats.org/spreadsheetml/2006/main";
        else if ("xml".equals(prefix)) return XMLConstants.XML_NS_URI;
        return XMLConstants.NULL_NS_URI;
    }

    // This method isn't necessary for XPath processing.
    public String getPrefix(String uri) {
        throw new UnsupportedOperationException();
    }

    // This method isn't necessary for XPath processing either.
    public Iterator getPrefixes(String uri) {
        throw new UnsupportedOperationException();
    }
});

// note that all the elements in the expression are prefixed with our namespace mapping!
XPathExpression expr = xpath.compile("/spreadsheet:workbook/spreadsheet:sheets/spreadsheet:sheet[1]");

// assuming you've got your XML document in a variable named doc...
Node result = (Node) expr.evaluate(doc, XPathConstants.NODE);

A voila ... Nyní máte svůj element uložen v proměnné result.

Caveat: pokud analyzujete svůj XML jako DOM se standardními třídami JAXP, nezapomeňte zavolat setNamespaceAware(true) na DocumentBuilderFactory. Jinak tento kód nebude fungovat!

56
stevevls

Všechny jmenné prostory, ze kterých chcete ve zdrojovém XML vybrat, musí být spojeny s předponou v jazyce Host. V jazyce Java/JAXP se to provádí zadáním URI pro každou předponu oboru názvů pomocí instance javax.xml.namespace.NamespaceContext. V sadě SDK bohužel existuje žádná implementaceNamespaceContext

Naštěstí je velmi snadné napsat vlastní:

import Java.util.HashMap;
import Java.util.Iterator;
import Java.util.Map;
import javax.xml.namespace.NamespaceContext;

public class SimpleNamespaceContext implements NamespaceContext {

    private final Map<String, String> PREF_MAP = new HashMap<String, String>();

    public SimpleNamespaceContext(final Map<String, String> prefMap) {
        PREF_MAP.putAll(prefMap);       
    }

    public String getNamespaceURI(String prefix) {
        return PREF_MAP.get(prefix);
    }

    public String getPrefix(String uri) {
        throw new UnsupportedOperationException();
    }

    public Iterator getPrefixes(String uri) {
        throw new UnsupportedOperationException();
    }

}

Použijte toto:

XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
HashMap<String, String> prefMap = new HashMap<String, String>() {{
    put("main", "http://schemas.openxmlformats.org/spreadsheetml/2006/main");
    put("r", "http://schemas.openxmlformats.org/officeDocument/2006/relationships");
}};
SimpleNamespaceContext namespaces = new SimpleNamespaceContext(prefMap);
xpath.setNamespaceContext(namespaces);
XPathExpression expr = xpath
        .compile("/main:workbook/main:sheets/main:sheet[1]");
Object result = expr.evaluate(doc, XPathConstants.NODESET);

Všimněte si, že i když první jmenný prostor neurčuje předponu ve zdrojovém dokumentu (tj. Je to výchozí jmenný prostor ) musíte ho stejně spojit s předponou. Váš výraz by pak měl odkazovat uzly v tomto oboru názvů pomocí předpony, kterou jste vybrali, jako je tato:

/main:workbook/main:sheets/main:sheet[1]

Názvy předpon, které se rozhodnete spojit s každým oborem názvů, jsou libovolné; nemusí odpovídat tomu, co se objevuje ve zdrojovém XML. Toto mapování je jen způsob, jak sdělit stroji XPath, že daný název prefixu ve výrazu koreluje se specifickým oborem názvů ve zdrojovém dokumentu.

34
Wayne Burkett

Pokud používáte Spring, již obsahuje org.springframework.util.xml.SimpleNamespaceContext. 

        import org.springframework.util.xml.SimpleNamespaceContext;
        ...

        XPathFactory xPathfactory = XPathFactory.newInstance();
        XPath xpath = xPathfactory.newXPath();
        SimpleNamespaceContext nsc = new SimpleNamespaceContext();

        nsc.bindNamespaceUri("a", "http://some.namespace.com/nsContext");
        xpath.setNamespaceContext(nsc);

        XPathExpression xpathExpr = xpath.compile("//a:first/a:second");

        String result = (String) xpathExpr.evaluate(object, XPathConstants.STRING);
3
kasi

Napsal jsem jednoduchou implementaci NamespaceContext ( zde ), která bere Map<String, String> jako vstup, kde key je prefix a value je jmenný prostor.

Sleduje NamespaceContext spesification a můžete vidět, jak to funguje v jednotkových testech .

Map<String, String> mappings = new HashMap<>();
mappings.put("foo", "http://foo");
mappings.put("foo2", "http://foo");
mappings.put("bar", "http://bar");

context = new SimpleNamespaceContext(mappings);

context.getNamespaceURI("foo");    // "http://foo"
context.getPrefix("http://foo");   // "foo" or "foo2"
context.getPrefixes("http://foo"); // ["foo", "foo2"]

Všimněte si, že má závislost na Google Guava

0
tomaj

Pokud neuvádím factory.setNamespaceAware(true);, pak xpath, který jste zmínil, funguje ve hře a bez jmenných prostorů. Nejste schopni vybrat věci "s jmenným prostorem určeným" pouze obecné xpaths. Jdi zjistit. Může se jednat o možnost:

 DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
 factory.setNamespaceAware(false);
0
rogerdpack

Ujistěte se, že odkazujete obor názvů v XSLT

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
             xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main"
             xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"       >
0
cordsen