Как вставить / заменить тег XML в XmlDocument?

У меня есть XmlDocument java, созданный с помощью Weblogic XmlDocument парсера.

Я хочу заменить содержимое тега в этом XMLDocument на свои собственные данные или вставить тег, если его там нет.

<customdata>
   <tag1 />
   <tag2>mfkdslmlfkm</tag2>
   <location />
   <tag3 />
</customdata>

Например, я хочу вставить URL-адрес в тег местоположения:

<location>http://something</location>

но в остальном оставьте XML как есть.

В настоящее время я использую XMLCursor :

    XmlObject xmlobj = XmlObject.Factory.parse(a.getCustomData(), options);
    XmlCursor xmlcur = xmlobj.newCursor();

    while (xmlcur.hasNextToken()) {
      boolean found = false;
      if (xmlcur.isStart() && "schema-location".equals(xmlcur.getName().toString())) {
        xmlcur.setTextValue("http://replaced");
        System.out.println("replaced");
        found = true;
      } else if (xmlcur.isStart() && "customdata".equals(xmlcur.getName().toString())) {
        xmlcur.push();
      } else if (xmlcur.isEnddoc()) {
        if (!found) {
          xmlcur.pop();
          xmlcur.toEndToken();
          xmlcur.insertElementWithText("schema-location", "http://inserted");
          System.out.println("inserted");
        }

      }
      xmlcur.toNextToken();
    }

Я попытался найти «быстрый» xquery способ сделать это, поскольку у XmlDocument него есть execQuery метод, но это оказалось не очень легко.

Есть ли у кого-нибудь способ лучше, чем этот? Это кажется немного сложным.

Ответов (4)

Решение

Как насчет подхода на основе XPath? Мне нравится этот подход, поскольку его логика очень проста для понимания. Код в значительной степени самодокументируется.

Если ваш xml-документ доступен вам как объект org.w3c.dom.Document (как возвращают большинство парсеров), вы можете сделать что-то вроде следующего:

// get the list of customdata nodes
NodeList customDataNodeSet = findNodes(document, "//customdata" );

for (int i=0 ; i < customDataNodeSet.getLength() ; i++) {
  Node customDataNode = customDataNodeSet.item( i );

  // get the location nodes (if any) within this one customdata node
  NodeList locationNodeSet = findNodes(customDataNode, "location" );

  if (locationNodeSet.getLength() > 0) {
    // replace
    locationNodeSet.item( 0 ).setTextContent( "http://stackoverflow.com/" );
  }
  else {
    // insert
    Element newLocationNode = document.createElement( "location" );
    newLocationNode.setTextContent("http://stackoverflow.com/" );
    customDataNode.appendChild( newLocationNode );
  }
}

А вот вспомогательный метод findNodes, который выполняет поиск XPath.

private NodeList findNodes( Object obj, String xPathString )
  throws XPathExpressionException {

  XPath xPath = XPathFactory.newInstance().newXPath();
  XPathExpression expression = xPath.compile( xPathString );
  return (NodeList) expression.evaluate( obj, XPathConstants.NODESET );
}

У вас должно получиться сделать это с помощью query

пытаться

 fn:replace(string,pattern,replace)

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

Я все еще хочу, чтобы был более простой способ, который был бы столь же эффективным?

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

Как насчет объектно-ориентированного подхода? Вы можете десериализовать XML в объект, установить значение местоположения для объекта, а затем сериализовать обратно в XML.

XStream упрощает это.

Например, вы должны определить основной объект, которым в вашем случае является CustomData (я использую общедоступные поля, чтобы упростить пример):

public class CustomData {
  public String tag1;
  public String tag2;
  public String location;
  public String tag3;
}

Затем вы инициализируете XStream:

XStream xstream = new XStream();
// if you need to output the main tag in lowercase, use the following line
xstream.alias("customdata", CustomData.class);  

Теперь вы можете создать объект из XML, установить поле местоположения для объекта и повторно сгенерировать XML:

CustomData d = (CustomData)xstream.fromXML(xml);
d.location = "http://stackoverflow.com";
xml = xstream.toXML(d);

Как это звучит?