Изменить URL-адрес пространства имен xml в Java

У меня есть Java REST API, и мы недавно сменили домен. API версируется, хотя до сих пор это включало добавление элементов в разные версии.

Я хотел бы изменить пространства имен, если кто-то вернется к предыдущим версиям, но я борюсь. Теперь, после некоторого взлома, я понял, что это, вероятно, связано с тем, что я меняю пространство имен xml, на которое фактически ссылаются. Я думал об этом как о текстовом документе, но я полагаю, что это не инструмент?

Итак, глядя на этот xml с URL-адресом пространства имен n # veg.com ->

<?xml version = "1.0" encoding = "UTF-8" standalone = "no"?>
<ns2:apple xmlns:ns2 = "http://veg.com/app/api/apple" xmlns:ns1 = "http://veg.com/app/api" xmlns:ns3 = "http://veg.com/app/api/apple/red" 
    xmlns:ns4 = "http://veg.com/app/banana" xmlns:ns5 = "http://veg.com/app/api/pear" xmlns:ns6 = "http://veg.com/app/api/orange" 
    ns1:created = "2016-05-23T16:47:55+01:00" ns1:href = "http://falseserver:8080/app/api/apple/1" ns1:id = "1">


    <ns2:name>granny smith</ns2:title>
    <ns2:flavour>sweet</ns2:status>
    <ns2:origin>southwest region</ns2:grantCategory>

    ...
</ns2:apple>

Я хотел бы изменить пространства имен на fruit.com. Это очень хакерский модульный тест, который показывает широкий подход, который я пробовал ...

 @Test
 public void testNamespaceChange() throws Exception {

        Document appleDoc = load("apple.xml");

        XPath xpath = XPathFactory.newInstance().newXPath();
        org.w3c.dom.Node node = (org.w3c.dom.Node) xpath.evaluate("//*[local-name()='apple']", appleDoc , XPathConstants.NODE);

        NamedNodeMap nodeMap = node.getAttributes();

        for (int i = 0; i < nodeMap.getLength(); i++) {
            if (nodeMap.item(i).getNodeName().startsWith("xmlns:ns")) {
                nodeMap.item(i).setTextContent( nodeMap.item(i).getNodeValue().replace( "veg.com", "fruit.com"));
            }
        }

        //Check values have been set 
        for (int i = 0; i < nodeMap.getLength(); i++) {
            System.out.println(nodeMap.item(i).getNodeName());
            System.out.println(nodeMap.item(i).getNodeValue());
            System.out.println("----------------");
        }

        StringWriter writer = new StringWriter();
        StreamResult result = new StreamResult(writer);
        Transformer transformer = TransformerFactory.newInstance().newTransformer();
        transformer.transform(new DOMSource(node), result);
        System.out.println("XML IN String format is: \n" +
      writer.toString());
}

Таким образом, в результате цикл элементов nodeMap показывает, что обновления выполняются.

т.е. все обновлено в соответствии с этими строками

xmlns:ns1
http://fruit.com/app/api
-------------------------------------------
xmlns:ns2
http://fruit.com/app/api/apple
-------------------------------------------
xmlns:ns3
http://fruit.com/app/api/apple/red
-------------------------------------------
...

но когда я распечатываю преобразованный документ, я получаю то, что вижу в ответе api ...

 <?xml version = "1.0" encoding = "UTF-8" standalone = "no"?>
    <ns2:apple xmlns:ns2 = "http://veg.com/app/api/apple" xmlns:ns1 = "http://veg.com/app/api" xmlns:ns3 = "http://fruit.com/app/api/apple/red" 
        xmlns:ns4 = "http://fruit.com/app/banana" xmlns:ns5 = "http://fruit.com/app/api/pear" xmlns:ns6 = "http://fruit.com/app/api/orange" 
        ns1:created = "2016-05-23T16:47:55+01:00" ns1:href = "http://falseserver:8080/app/api/apple/1" ns1:id = "1">

Близкие (и расположенные ниже по иерархии) пространства имен были изменены, но ns1 и ns2 остались неизменными.

Может ли кто-нибудь сказать мне, почему и есть ли у меня простой способ обновить их? Я предполагаю, что следующим шагом для меня может быть потоковая передача xml-документа в строку, обновление их как текста, а затем перезагрузка как xml-документ, но я надеюсь, что я поражен, и есть более элегантное решение?

Что именно вы имеете в виду под «изменением пространства имен»? Вы хотите преобразовать XML-документ в «старом» пространстве имен в XML-документ в «новом» пространстве имен?

lexicore 17.04.2018 18:11

На самом деле все, что я хочу сделать, это обновить атрибуты пространства имен в основном узле. Итак, я хочу обновить содержимое атрибутов xmlns: ns1, xmlns: ns2, xmlns: ns3 и т. д. (Измените URL-адрес на наш новый домен). Мне потребовалось довольно много времени, чтобы понять, что это какой-то код блокирует это изменение (org.w3c.dom или javax.xml.transform)?

gringogordo 17.04.2018 18:27

Проблема в том, что вы подходите к этому с синтаксической точки зрения. Хотя это может сработать в тех же случаях, логически видно, что пространства имен - это не просто некоторые атрибуты xmlns:someprefix, разбросанные вокруг. Я думаю, что самый простой правильный способ решить эту проблему - это преобразование XSLT, которое переназначает определенные пространства имен.

lexicore 17.04.2018 19:58
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
1
3
987
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я бы решил это с помощью XSLT следующим образом:

<xsl:stylesheet version = "1.0" xmlns:xsl = "http://www.w3.org/1999/XSL/Transform">

  <xsl:template match = "*[namespace-uri()='http://veg.com/app/api/apple']" priority = "1">
    <xsl:element name = "local-name()" namespace = "http://fruit.com/app/api/apple">
      <xsl:apply-templates select = "@*|node()"/>
    </xsl:element>
  </xsl:template>

  <xsl:template match = "@*|node()">
    <xsl:copy>
      <xsl:apply-templates select = "@*|node()"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

Эта таблица стилей объединяет преобразование идентичности с шаблоном, который изменяет пространство имен элементов в http://veg.com/app/api/apple на http://fruit.com/app/api/apple.

Я думаю, что это намного проще того Java-кода, который у вас есть. Вы также будете более гибкими, если обнаружите, что у вас больше различий между версиями вашего XML, кроме пространств имен.

Считайте, что это набросок. Я написал книгу по XSLT около 15 лет назад, но не использовал XSLT более 6 или 7 лет.

Хорошо, спасибо, я думаю, ты прав. Я немного борюсь с синтаксисом, но я потрачу день, пытаясь изучить xslt, и, если необходимо, опубликую еще один вопрос, когда я сделаю респектабельную попытку.

gringogordo 18.04.2018 11:10

Я решил проблему с этим. Единственное изменение, которое мне пришлось сделать, это то, что имя элемента нужно было заключить в фигурные скобки для поддержки его вычислений. Пример: <xsl:element name = "{local-name()}" namespace = "http://fruit.com/app/api/apple">

palto 21.04.2021 09:23

Другие вопросы по теме