Проблема с пространствами имен XML

Проблема с пространствами имен XML

Очередная сложность, возникшая при решении задачи в проекте, побудила к написанию этой заметки. Скорее, это вклад в свою базу знаний и желание поделиться опытом, нежели учебное пособие для кого бы то ни было. Поэтому прошу всех желающих оставить в комментариях свои способы решения проблемы.

Итак, к проблеме. Стоит задача разобрать XML и по определенному набору тэгов и их атрибутов вернуть булеву переменную. Разбираемая XML передается как строковая переменная. XML документ построен с применением пространств имен, названия которых (как и сам документ) сгенерированы автоматически. А это, конечно, не исключает возможности изменения названия пространств имен.

Нужно пробежаться по множеству тэгов, но основная сложность в том, что теги эти могут иметь различные пространства имен. Пример XML:

        <ns2:format>
            <ns3:technology basetype="smem">
                <ns3:facets>
                    <ns3:facet>knb_1</ns3:facet>
                </ns3:facets>
                <ns3:specification>
                </ns3:specification>
            </ns3:technology>
            <ns2:quality>med</ns2:quality>
        </ns2:format>
        <ns2:format>
            <ns2:technology basetype="h264_aac_3gp_http_na_na">
                <ns3:facets>
                    <ns3:facet>knb_2</ns3:facet>
                </ns3:facets>
                <ns2:specification>
                </ns2:specification>
            </ns2:technology>
            <ns2:quality>high</ns2:quality>
        </ns2:format>

Как видно из примера, technology и specification определены в обоих пространствах имен (в ns2 и ns3). Чтобы сделать пробег по всем элементам technology, я собираюсь использовать вот такую конструкцию:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;
db = dbf.newDocumentBuilder();

InputSource is = new InputSource();
is.setCharacterStream(new StringReader(streamTmd.asXml()));

Document doc = db.parse(is);
NodeList elements = doc.getElementsByTagName("technology");

Но такой вызов возвращает пустой результат — elements не содержит элементов.

Поизучав вопрос с использованием парсеров применительно к пространствам имен, нашел вот такое решение:

DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = null;

dbf.setNamespaceAware(true);  //важный атрибут
db = dbf.newDocumentBuilder();
InputSource is = new InputSource();
is.setCharacterStream(new StringReader(streamTmd.asXml()));

Document doc = db.parse(is);
//вызов с заданием namespace
NodeList elements = doc.getElementsByTagNameNS("*", "technology");

В четвертой строке обязательно нужно задать, что наш парсер должен обращать внимание на пространства имен. А чтобы получить все тэги по определенному имени вне зависимости от их пространства имен, нужно вызвать соответствующий метод со звездочкой вместо namespace, что будет означать «учитывать все namespaces» (строка 11).