inserir/substituir tag XML em XmlDocument
Pergunta
eu tenho um XmlDocument
em java, criado com o Weblogic XmlDocument
analisador.
Quero substituir o conteúdo de uma tag neste XMLDocument
com meus próprios dados ou insira a tag se ela não estiver lá.
<customdata>
<tag1 />
<tag2>mfkdslmlfkm</tag2>
<location />
<tag3 />
</customdata>
Por exemplo, quero inserir algum URL na tag de localização:
<location>http://something</location>
mas caso contrário, deixe o XML como está.
Atualmente eu uso um 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();
}
Eu tentei encontrar um "rápido" xquery
maneira de fazer isso desde o XmlDocument
tem um execQuery
método, mas não achei muito fácil.
Alguém tem uma maneira melhor do que esta?Parece um pouco elaborado.
Solução
Que tal uma abordagem baseada em XPath?Gosto dessa abordagem porque a lógica é muito fácil de entender.O código é praticamente autodocumentado.
Se o seu documento xml estiver disponível como um objeto org.w3c.dom.Document (como a maioria dos analisadores retorna), você poderá fazer algo como o seguinte:
// 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 );
}
}
E aqui está o método auxiliar findNodes que faz a pesquisa 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 );
}
Outras dicas
Que tal uma abordagem orientada a objetos?Você pode desserializar o XML para um objeto, definir o valor do local no objeto e serializar novamente para XML.
XStream torna isso muito fácil.
Por exemplo, você definiria o objeto principal, que no seu caso é CustomData (estou usando campos públicos para manter o exemplo simples):
public class CustomData {
public String tag1;
public String tag2;
public String location;
public String tag3;
}
Então você inicializa o XStream:
XStream xstream = new XStream();
// if you need to output the main tag in lowercase, use the following line
xstream.alias("customdata", CustomData.class);
Agora você pode construir um objeto a partir de XML, definir o campo de localização no objeto e gerar novamente o XML:
CustomData d = (CustomData)xstream.fromXML(xml);
d.location = "http://stackoverflow.com";
xml = xstream.toXML(d);
Como isso soa?
Se você não conhece o esquema, a solução XStream provavelmente não é a melhor opção.Pelo menos o XStream está no seu radar agora, pode ser útil no futuro!
Você deve ser capaz de fazer isso com query
tentar
fn:replace(string,pattern,replace)
Eu sou novo no xquery e descobri que é uma linguagem de consulta difícil de trabalhar, mas funciona bem quando você supera a curva de aprendizado inicial.
Eu ainda gostaria que houvesse uma maneira mais fácil e tão eficiente?