プログラマメモ2 - programmer no memo2

特定のタグを消したい 2012/12/10

Javaです。
「XMLから特定のタグだけを消して、その子のノードはそのままにしたい」
順繰りみていきながら、特定のタグで特定なクラス名だけを削除するという実装にしています。
むずかしかったのはその子の要素をそのままにしたいので、子のクローンを用意して、消したいノードの場所に次々と足すということをしています。気がついたらなるほどねということのなのでしょうけども。

はじめ子のクローンでなくそのもので足していたったら、JVMがぽこーんと落ちたので(正確には、JavaFXの上のHTMLEditorなのですが) ようやくクローンしないといけないことに気がついたのでした。


/** * * @param document * @param rootNode * @param tag * @param className */ public static void removeNodeByTagNameAndClassName(Document document, Node rootNode, String tag, String className) { NodeList nodeList = rootNode.getChildNodes(); for (int i = 0; i < nodeList.getLength(); i++) { Node node = nodeList.item(i); removeNodeByTagNameAndClassName(document, node, tag, className); if (Node.ELEMENT_NODE == node.getNodeType()) { Element element = (Element) node; Attr attr = element.getAttributeNode("class"); if (element.getTagName().toUpperCase() .equals(tag.toUpperCase()) && attr != null && attr.getValue().equals(className)) { NodeList nodeList2 = element.getChildNodes(); Node parent = element.getParentNode(); for (int j = 0; j < nodeList2.getLength(); j++) { Node newChild = nodeList2.item(j).cloneNode(true); parent.insertBefore(newChild, element); } parent.removeChild(element); } } } }

xpathです。ノード名があれやこれやというふうにORで指定したい場合 - local-name()を使って 2010/03/28

XMLです。XPathです。
忘れないうちにメモしておこう。

ノード名があれやこれやというふうにORで指定したい場合

下記のような感じで書けます。

//Word/*[local-name()='Furigana' or local-name()='Roman']

XPath生成もどきもどきもどき 2008/09/05

Javaです。
既に出来上がったXMLをパースするプログラムを書く際に、必要な箇所だけをXPathとかでとりたいなぁというときに、パスが既に存在するXMLから生成できたら便利かなぁ、というわけで作成。

不完全だけど、パスの雰囲気はでてるかな。

package xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Set;
import java.util.TreeSet;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class TestXPathGenUtils {

public static void main(String[] args) throws URISyntaxException,
FileNotFoundException, SAXException, IOException,
ParserConfigurationException {

URL url = TestXPathGenUtils.class.getResource("test.xml");

File file = new File(url.toURI());
DocumentBuilder builder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();

Set<String> set = new TreeSet<String>();
XPathGenUtils.generatePath(builder.parse(new InputSource(new FileInputStream(file))), set);

for (String s : set) {
System.out.println(s);
}

}

}


package xml;

import java.util.Set;

import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

/**
* <p>
* XPath生成もどき
* </p>
*
* @author nakawaka
*
*/
public abstract class XPathGenUtils {

public static void generatePath(Document document, Set<String> set) {
innerGeneratePath(document.getDocumentElement(), set);
}

public static void generatePath(Element element, Set<String> set) {
innerGeneratePath(element, set);
}

static void innerGeneratePath(Node node, Set<String> set) {
switch (node.getNodeType()) {

case Node.DOCUMENT_NODE:
generatePath(((Document) node).getDocumentElement(), set);
return;
case Node.ELEMENT_NODE:

Element element = (Element) node;

NamedNodeMap namedNodeMap = element.getAttributes();
for (int i = 0; i < namedNodeMap.getLength(); ++i) {
innerGeneratePath(namedNodeMap.item(i), set);
}

NodeList nodeList = element.getChildNodes();
for (int i = 0; i < nodeList.getLength(); ++i) {
innerGeneratePath(nodeList.item(i), set);
}

return;
case Node.TEXT_NODE:
set.add(p(node, new StringBuilder()));
return;
case Node.CDATA_SECTION_NODE:
return;
case Node.ENTITY_REFERENCE_NODE:
return;
case Node.ATTRIBUTE_NODE:
set.add(p(node, new StringBuilder()));
return;
}

}

public static String p(Node node, StringBuilder builder) {

// System.out.println(parent.getNodeName());

if (node.getNodeType() == Node.DOCUMENT_NODE)
return builder.toString();

switch (node.getNodeType()) {
case Node.TEXT_NODE:
builder.insert(0, "/" + "text()");
return p(node.getParentNode(), builder);
case Node.CDATA_SECTION_NODE:
break;
case Node.ENTITY_REFERENCE_NODE:
break;
case Node.ATTRIBUTE_NODE:
builder.insert(0, "/@" + node.getNodeName() + "");
Attr attr = (Attr) node;
return p(attr.getOwnerElement(), builder);
}

builder.insert(0, "/" + node.getNodeName());
return p(node.getParentNode(), builder);
}
}


出来上がったパスは下のコードで実行
package xml;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

public class TestReadXml {

public static void main(String[] args) throws URISyntaxException,
ParserConfigurationException, FileNotFoundException, SAXException,
IOException, XPathExpressionException {

URL url = TestReadXml.class.getResource("test.xml");

File file = new File(url.toURI());
DocumentBuilder builder = DocumentBuilderFactory.newInstance()
.newDocumentBuilder();
Document document = builder.parse(new InputSource(new FileInputStream(
file)));

System.out.println(string(document, compile("パステストここに入れる")));
}

static final XPath xpath = XPathFactory.newInstance().newXPath();

static XPathExpression compile(String expression)
throws XPathExpressionException {
return xpath.compile(expression);
}

static String string(Document root, XPathExpression expr)
throws XPathExpressionException {
return (String) expr.evaluate(root, XPathConstants.STRING);
}

}

XPath エレメント名が〜でないものを取得 2008/05/03

XPathを使って、エレメント名が〜でないものを取得します。
どういうことかといいますと、


<root>
<a>a1</a>
<b1>b1</b1>
<b2>b2</b2>
<b3>b3</b3>
</root>

上記のような構造のXML文書があった場合なのですが、aというエレメント名でないエレメントを取得したい場合にどうXPathを書けばいいか、考えて、ある集合から単純にaではないという集合(NodeSet)を求めればいいだろうという、結論に達したのでした。たいしたことではありませんが....

というわけで下記のようなXPathで取得できます。
*[local-name()!='a']


このlocal-name関数は、ノード集合関数であり、なにやらエレメント名を返してくれる関数のようです。

ちなみに、用語は正確に使いたいのですが、いまいちよくわかってないのが、XML。
お仕事では毎日のようにみているXMLなのですが、正確に説明しようとすると、自信がないです。
エレメント名という言い方をしました、タグとかいってしまっていいのかどうかとか...

参考

java でxml xpathで、ネームスペース 2007/11/14

Java XPath API


java5以上です。
ネームスペースを使ってXPathを使って値をとるサンプルです。

package a;

import java.io.IOException;

import java.net.URISyntaxException;

import javax.xml.XMLConstants;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpression;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import java.util.Iterator;
import javax.xml.namespace.NamespaceContext;

public class TestMain {

public static void main(String[] args) throws SAXException, IOException,
XPathExpressionException, ParserConfigurationException,
URISyntaxException {

DocumentBuilderFactory domFactory = DocumentBuilderFactory
.newInstance();
domFactory.setNamespaceAware(true);// 重要!!
DocumentBuilder builder = domFactory.newDocumentBuilder();
Document doc = builder.parse(TestMain.class.getResource("a.xml")
.toURI().toString());
a(doc);
System.out.println("----");
b(doc);

}

static void a(Document doc) throws XPathExpressionException {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
XPathExpression expr = xpath
.compile("//book[@year='1969']/title/text()");

Object result = expr.evaluate(doc, XPathConstants.STRING);
System.out.println(result);

}

static void b(Document doc) throws XPathExpressionException {
XPathFactory factory = XPathFactory.newInstance();
XPath xpath = factory.newXPath();
xpath.setNamespaceContext(new MyNamespaceContext());

XPathExpression expr = xpath
.compile("//my:book[@year='2007']/my:title/text()");
Object result = expr.evaluate(doc, XPathConstants.STRING);
System.out.println(result);
}

static class MyNamespaceContext implements NamespaceContext {
public String getNamespaceURI(String prefix) {
if (prefix == null)
throw new NullPointerException("Null prefix");
else if ("my".equals(prefix))
return "http://deiji.jp";
else if ("xml".equals(prefix))
return XMLConstants.XML_NS_URI;
return XMLConstants.NULL_NS_URI;
}

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

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

}

}



<my xmlns="http://deiji.jp">
<book year="1969">
<title>aaaaa</title>
<author>nakawaka</author>
</book>
<book year="2007">
<title>bbbbb</title>
<author>shigeto</author>
</book>
</my>

いまさらながら感がありますが、java1.4でutf-8のBOM付XMLファイルをパースすると失敗するっぽい 2007/11/05
2007/11/06

javaです。バージョンは1.4です。

どうもutf-8のBOM付XMLファイルをパースすると失敗するようです。
下記のようなエラーがでるようです。

org.xml.sax.SAXParseException: ドキュメントのルート要素がありません。
at org.apache.crimson.parser.Parser2.fatal(Parser2.java:3376)
at org.apache.crimson.parser.Parser2.fatal(Parser2.java:3364)
at org.apache.crimson.parser.Parser2.parseInternal(Parser2.java:668)
at org.apache.crimson.parser.Parser2.parse(Parser2.java:337)
at org.apache.crimson.parser.XMLReaderImpl.parse(XMLReaderImpl.java:448)
at org.apache.crimson.jaxp.DocumentBuilderImpl.parse(DocumentBuilderImpl.java:185)
at javax.xml.parsers.DocumentBuilder.parse(DocumentBuilder.java:76)
at x.A.a(A.java:68)
at x.A.main(A.java:24)
Exception in thread "main"


utf-8のBOMなしでやるとうまくいきました。

使用しているコードは、
public static void a() throws ParserConfigurationException, SAXException,
IOException {

URL url = A.class.getResource("b.xml");
InputStream inputStream = url.openStream();
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();

System.out.println(">>" + builder);

Document doc = builder.parse(inputStream);

}



それで、H氏からの情報
かわりに Apache Xerces-J を使うとうまくいきます。


あと、

Xerces-J-bin.2.9.1.zip or tar.gz から xercesImpl.jar と xml-apis.jar を取り出し、
java 起動時に、これら二つを読み込みつつ、

-Djavax.xml.parsers.DocumentBuilderFactory=<DocumentBuilderFactoryImpl>
-Djavax.xml.transform.TransformerFactory=<TransformerFactoryImpl>


もう1.4からjava5の世界に移行しないと....

jxpath xmlns - ナントナクワカッタ 2007/09/18

jxpathです。

ルートのタグにxmlnsがある場合の、使用方法

不正確です。

ネームスペースが指定されている場合の注意点。


<?xml version="1.0" encoding="UTF-8"?>
<aaa xmlns='http://xxxx/'>
<a>OKAAAA</a>
</aaa>

って感じのXMLがある場合、

JXPathContextに
context.registerNamespace("A", "http://xxxx/");
って感じのことをしてXPathを記述する。

こんな感じ、
context.getValue("/A:aaa/A:a")

ネームスペースしないと例外でます。

数値文字参照、文字実体参照について。 2007/08/09

数値文字参照、文字実体参照について。

カンマは、数値文字参照で表現すると&#x2cです。

American Standard Code for Information Interchange - Wikipedia
文字参照 - Wikipedia

XML文書内に改行を含めたい。 2007/07/24

XMLファイルに改行を含めたい場合、文字参照?を使用します。


具体的には、
スペース文字(#x20)
改行(#xA)
タブ(#x9)
復帰+改行(#xD#xA)
復帰(#xD)
を使用するようですね。

それで、XMLには下記のように記述します。

<?xml version="1.0" encoding="UTF-8"?>
<a>
<b prop="スペース&#x20;です。" />
<b prop="改行&#xA;です。" />
<b prop="タブ&#x9;です。" />
<b prop="復帰+改行&#xD;&#xA;です。" />
<b prop="復帰&#xD;です。" />
</a>




javaのプログラムを利用してパースします。
結果は、
nodename:[#text] nodevalue:[
]
nodename:[b] nodevalue:[null]
attr:[prop] attrvalue:[スペース です。]
nodename:[#text] nodevalue:[
]
nodename:[b] nodevalue:[null]
attr:[prop] attrvalue:[改行
です。]
nodename:[#text] nodevalue:[
]
nodename:[b] nodevalue:[null]
attr:[prop] attrvalue:[タブ です。]
nodename:[#text] nodevalue:[
]
nodename:[b] nodevalue:[null]
attr:[prop] attrvalue:[復帰+改行
です。]
nodename:[#text] nodevalue:[
]
nodename:[b] nodevalue:[null]
attr:[prop] attrvalue:[復帰
です。]
nodename:[#text] nodevalue:[
]


下記のコードを利用しました。

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

public class TestReadXml {

public static void main(String[] args) throws ParserConfigurationException,
SAXException, IOException {
a();
}

public static void println(String s) {
System.out.println(s);
}

public static void printNode(NodeList list) {
if (list == null)
return;
Node node;
for (int i = 0; (node = list.item(i)) != null; i++) {
println("nodename:[" + node.getNodeName() + "] nodevalue:[" + node.getNodeValue() + "]");
printAttr(node);
printNode(node.getChildNodes());
}
}

public static void printAttr(Node node) {
NamedNodeMap attrMap = node.getAttributes();
if(attrMap == null) return;
int attrs = attrMap.getLength();
for (int i = 0; i < attrs; i++) {
Node attr = attrMap.item(i);
println(" attr:[" + attr.getNodeName() + "] attrvalue:[" + attr.getNodeValue() + "]");
}
}

public static void a() throws ParserConfigurationException, SAXException,
IOException {
InputStream inputStream = TestReadXml.class
.getResourceAsStream("a.xml");

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(inputStream);

Element element = doc.getDocumentElement();
NodeList list = element.getChildNodes();
printNode(list);
}
}


XMLを出力 2007/04/06

XMLデータをインデントして出力するためのテスト。
XMLSerializerを使うのが簡単なのだが、deprecatedになっている。
使ったxercesのバージョンは2.9.0



import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;

import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.transform.stream.StreamSource;

import org.apache.xerces.dom.CoreDOMImplementationImpl;
import org.apache.xerces.dom.CoreDocumentImpl;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.DOMImplementation;
import org.w3c.dom.Document;
import org.w3c.dom.ls.LSSerializer;
import org.xml.sax.SAXException;

public class TestXml {
static String xml = "<root><test name=\"01\"><item>item1</item></test></root>";

public static void main(String[] args) throws UnsupportedEncodingException,
SAXException, IOException, ParserConfigurationException {

DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();

Document document = factory.newDocumentBuilder().parse(
new ByteArrayInputStream(xml.getBytes("utf-8")));
System.out.println(">>>>XMLSerializerを使用した結果");
System.out.println(serialize(document));
System.out.println(">>>>Transformerを使用した結果");
System.out.println(serialize2(document));
System.out.println(">>>>Transformerを使用した結果");
System.out.println(serialize3(xml));
System.out.println(">>>>LSSerializerを使用した結果");
System.out.println(serialize4(document));
}

protected static String serialize(Document document) {
try {
OutputFormat format = new OutputFormat(document, "utf-8", true);
StringWriter writer = new StringWriter();
XMLSerializer serializer = new XMLSerializer(writer, format);
serializer.serialize(document);
String result = writer.toString();
return result;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}

protected static String serialize2(Document document) {
try {

Source source = new DOMSource(document);
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();

Writer writer = new StringWriter();
Result result = new StreamResult(writer);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
transformer.transform(source, result);

return writer.toString();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
}
return null;
}

protected static String serialize3(String s) {
try {

Source source = new StreamSource(new ByteArrayInputStream(s
.getBytes("utf-8")));
TransformerFactory factory = TransformerFactory.newInstance();
Transformer transformer = factory.newTransformer();
Writer writer = new StringWriter();
Result result = new StreamResult(writer);
transformer.setOutputProperty(OutputKeys.INDENT, "yes");

transformer.transform(source, result);

return writer.toString();
} catch (TransformerConfigurationException e) {
e.printStackTrace();
} catch (TransformerException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}

protected static String serialize4(Document document) {

CoreDocumentImpl coreDocumentImpl = new CoreDocumentImpl();
DOMImplementation implementation = coreDocumentImpl.getImplementation();
CoreDOMImplementationImpl impl2 = (CoreDOMImplementationImpl) implementation;
LSSerializer serializer = impl2.createLSSerializer();

return serializer.writeToString(document);
}

}


>>>>XMLSerializerを使用した結果
<?xml version="1.0" encoding="utf-8"?>
<root>
<test name="01">
<item>item1</item>
</test>
</root>

>>>>Transformerを使用した結果
<?xml version="1.0" encoding="UTF-8"?>
<root>
<test name="01">
<item>item1</item>
</test>
</root>

>>>>Transformerを使用した結果
<?xml version="1.0" encoding="UTF-8"?>
<root>
<test name="01">
<item>item1</item>
</test>
</root>

>>>>LSSerializerを使用した結果
<?xml version="1.0" encoding="UTF-16"?>
<root><test name="01"><item>item1</item></test></root>