ボンジュール・マドモアゼル

本サイトの情報は自己責任にてご利用下さい。

[Web開発] Java における各種 XML シリアライザの出力結果

 
http://monado.dtiblog.com/blog-entry-175.html の続き。

検証内容
子要素として、接頭辞が未指定で作成された rect 要素を持つルート要素 svg:svg を作成し、
以下の各シリアライザでそれぞれどのようにシリアライズされるかを確認する。


また、シリアライザによって DOM ツリーが変更されることを見るため、
要素 svg:svg を都度、再生成し、シリアライズされた XML 文字列の前後に要素 svg:svg のDOMツリーを印刷する。

注意事項
Xerces の XMLSerializer は Javadocで以下のように書かれてる通り既に非推奨になっている。
Deprecated. This class was deprecated in Xerces 2.9.0. It is recommended that new applications use the DOM Level 3 LSSerializer or JAXP's Transformation API for XML (TrAX) for serializing XML.
推奨されていません。このクラスは、Xercesは2.9.0で非推奨になりました。新しいアプリケーションでは、XMLをシリアル化するために XMLDOM Level 3LSSerializer または JAXPTransformation API(TrAX) を使用することをお勧めします。


検証用コード

import java.io.ByteArrayOutputStream;

import java.io.StringWriter;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.apache.xml.serializer.OutputPropertiesFactory;
import org.w3c.dom.DOMConfiguration;
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.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSOutput;
import org.w3c.dom.ls.LSSerializer;

public class NSTest {

static final String SVG_NS = "http://www.w3.org/2000/svg";

public static abstract class TestTemplate {

private String title;

public TestTemplate(String title) {
this.title = title;
}

abstract public String serialize(Element element) throws Exception;

public void runTest() throws Exception {
printTitle();

Element element = createTestElement();
printNode(element, 0);

System.out.println(serialize(element));

System.out.println();
printNode(element, 0);
}

public void printTitle() {
System.out.println(repeat("#", 80));
System.out.println("\t\t" + title);
System.out.println(repeat("#", 80));
System.out.println();
}

public Element createTestElement() throws Exception {

DocumentBuilderFactory dbfactory = DocumentBuilderFactory
.newInstance();
dbfactory.setNamespaceAware(true);
DocumentBuilder builder = dbfactory.newDocumentBuilder();
Document doc = builder.newDocument();

// 要素rootを作成する.
Element root = doc.createElementNS(SVG_NS, "svg:svg");
// 要素 el1 は名前空間プレフィックスを指定せずに作成する.
Element el1 = doc.createElementNS(SVG_NS, "rect");
root.appendChild(el1);

return root;
}
}

public static void main(String[] argv) throws Exception {

/**********************************************************************/
// JAXP Transformer クラスを使ったシリアライズ.
/**********************************************************************/
(new TestTemplate("JAXP's Transformer") {

public String serialize(Element element) throws Exception {

TransformerFactory tff = TransformerFactory.newInstance();
Transformer transformer = tff.newTransformer();

// XML宣言の省略を指示する.
transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
// インデント出力を指示する。
transformer.setOutputProperty(OutputKeys.INDENT, "yes");
// インデント幅を設定する。
transformer.setOutputProperty(
OutputPropertiesFactory.S_KEY_INDENT_AMOUNT, "4");

// シリアライズする DOM オブジェクト
DOMSource src = new DOMSource(element);

// シリアライズした結果を出力する先
StringWriter writer = new StringWriter();
StreamResult output = new StreamResult(writer);

// シリアライズ
transformer.transform(src, output);

writer.flush();
writer.close();
return writer.toString();
}
}).runTest();

/**********************************************************************/
// DOM Level 3 Load and Save API を使ったシリアライズ.
/**********************************************************************/
(new TestTemplate("DOM Level 3 Load and Save API") {

public String serialize(Element element) throws Exception {

DOMImplementationRegistry reg = DOMImplementationRegistry.newInstance();
DOMImplementationLS impLS = (DOMImplementationLS) reg.getDOMImplementation("LS");

if (impLS == null) {
System.err.println("DOM3 Load and Save feature is not supported.");
return "";
}

// LSSerializer クラスのインスタンス取得
LSSerializer lsserializer = impLS.createLSSerializer();

DOMConfiguration conf = lsserializer.getDomConfig();
// XML宣言の省略を指示する。
conf.setParameter("xml-declaration", false);
// インデント出力を指示する。
conf.setParameter("format-pretty-print", true);

// (参考) DOMConfiguration のプロパティについては次を参照。
// http://www.w3.org/TR/DOM-Level-3-LS/load-save.html#LS-LSSerializer-config

// LSOutput クラスのインスタンスを作成し, 出力先設定
LSOutput lsoutput = impLS.createLSOutput();
lsoutput.setEncoding("UTF-8");

ByteArrayOutputStream os = new ByteArrayOutputStream();
lsoutput.setByteStream(os);

// シリアライズ
lsserializer.write(element, lsoutput);

os.flush();
os.close();
return os.toString("UTF-8");
}
}).runTest();

/**********************************************************************/
// Xerces の XMLSerializer によるシリアライズ.
/**********************************************************************/
(new TestTemplate("DOM Level 3 Load and Save API") {

public String serialize(Element element) throws Exception {

OutputFormat format = new OutputFormat();
// XML宣言の省略を指示する。
format.setOmitXMLDeclaration(true);
// インデント出力を指示する。
format.setIndenting(true);
// インデント幅を設定する。
format.setIndent(4);

StringWriter writer = new StringWriter();
XMLSerializer serializer = new XMLSerializer(writer, format);

// シリアライズ
serializer.serialize(element);

writer.flush();
writer.close();
return writer.toString();
}
}).runTest();
}

private static void printNode(Node nd, int depth) {
String indent = repeat("\t", depth);

StringBuilder sb = new StringBuilder();
sb.append(indent + "getNodeType():\t\t" + nodeTypeNames[nd.getNodeType()] + "\n");
sb.append(indent + "getNodeName():\t\t" + nd.getNodeName() + "\n");
sb.append(indent + "getPrefix():\t\t" + nd.getPrefix() + "\n");
sb.append(indent + "getLocalName():\t\t" + nd.getLocalName() + "\n");
sb.append(indent + "getNamespaceURI():\t" + nd.getNamespaceURI() + "\n");

switch (nd.getNodeType()) {
case Node.TEXT_NODE:
case Node.ATTRIBUTE_NODE:
sb.append(indent + "getNodeValue():\t\t" + nd.getNodeValue() + "\n");
}

if (nd.getNodeType() == Node.ELEMENT_NODE) {
sb.append(indent + "getTagName():\t\t" + ((Element) nd).getTagName() + "\n");
}

System.out.println(sb.toString());

NamedNodeMap nmap = nd.getAttributes();
if (nmap != null) {
for (int i = 0; i < nmap.getLength(); i++) {
printNode(nmap.item(i), depth + 1);
}
}

NodeList nlist = nd.getChildNodes();
for (int i = 0; i < nlist.getLength(); i++) {
printNode(nlist.item(i), depth + 1);

}
}

private static String repeat(String str, int count) {
return new String(new char[count]).replace("\0", str);
}

// nodeType 数値から名前(文字列)へのマッピング
static String[] nodeTypeNames = { "",
"ELEMENT_NODE", "ATTRIBUTE_NODE", "TEXT_NODE",
"CDATA_SECTION_NODE", "ENTITY_REFERENCE_NODE",
"ENTITY_NODE", "PROCESSING_INSTRUCTION_NODE",
"COMMENT_NODE", "DOCUMENT_NODE", "DOCUMENT_TYPE_NODE",
"DOCUMENT_FRAGMENT_NODE", "NOTATION_NODE" };
}


出力結果
強調表示部がシリアライズされたXML文字列。
DOM Level 3 Load and Save API では、要素に名前空間宣言が属性に追加され、
シリアライズ前後で、DOMツリーが変わっている。
################################################################################

JAXP's Transformer
################################################################################

getNodeType(): ELEMENT_NODE
getNodeName(): svg:svg
getPrefix(): svg
getLocalName(): svg
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): svg:svg

getNodeType(): ELEMENT_NODE
getNodeName(): rect
getPrefix(): null
getLocalName(): rect
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): rect

<svg:svg xmlns:svg="http://www.w3.org/2000/svg">
<rect xmlns="http://www.w3.org/2000/svg"/>
</svg:svg>



getNodeType(): ELEMENT_NODE
getNodeName(): svg:svg
getPrefix(): svg
getLocalName(): svg
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): svg:svg

getNodeType(): ELEMENT_NODE
getNodeName(): rect
getPrefix(): null
getLocalName(): rect
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): rect

################################################################################
DOM Level 3 Load and Save API
################################################################################

getNodeType(): ELEMENT_NODE
getNodeName(): svg:svg
getPrefix(): svg
getLocalName(): svg
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): svg:svg

getNodeType(): ELEMENT_NODE
getNodeName(): rect
getPrefix(): null
getLocalName(): rect
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): rect

<svg:svg xmlns:svg="http://www.w3.org/2000/svg">
<rect xmlns="http://www.w3.org/2000/svg"/>
</svg:svg>



getNodeType(): ELEMENT_NODE
getNodeName(): svg:svg
getPrefix(): svg
getLocalName(): svg
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): svg:svg

getNodeType(): ATTRIBUTE_NODE
getNodeName(): xmlns:svg
getPrefix(): xmlns
getLocalName(): svg
getNamespaceURI(): http://www.w3.org/2000/xmlns/
getNodeValue(): http://www.w3.org/2000/svg

getNodeType(): TEXT_NODE
getNodeName(): #text
getPrefix(): null
getLocalName(): null
getNamespaceURI(): null
getNodeValue(): http://www.w3.org/2000/svg

getNodeType(): ELEMENT_NODE
getNodeName(): rect
getPrefix(): null
getLocalName(): rect
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): rect

getNodeType(): ATTRIBUTE_NODE
getNodeName(): xmlns
getPrefix(): null
getLocalName(): xmlns
getNamespaceURI(): http://www.w3.org/2000/xmlns/
getNodeValue(): http://www.w3.org/2000/svg

getNodeType(): TEXT_NODE
getNodeName(): #text
getPrefix(): null
getLocalName(): null
getNamespaceURI(): null
getNodeValue(): http://www.w3.org/2000/svg

################################################################################
DOM Level 3 Load and Save API
################################################################################

getNodeType(): ELEMENT_NODE
getNodeName(): svg:svg
getPrefix(): svg
getLocalName(): svg
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): svg:svg

getNodeType(): ELEMENT_NODE
getNodeName(): rect
getPrefix(): null
getLocalName(): rect
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): rect

<svg:svg>
<rect/>
</svg:svg>



getNodeType(): ELEMENT_NODE
getNodeName(): svg:svg
getPrefix(): svg
getLocalName(): svg
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): svg:svg

getNodeType(): ELEMENT_NODE
getNodeName(): rect
getPrefix(): null
getLocalName(): rect
getNamespaceURI(): http://www.w3.org/2000/svg
getTagName(): rect

[Web開発] テストコード その2

 
以下、シリアライザでプレフィックスを調整しているのが確認できる JavaScript 用のテストコード。
テスト内容については http://monado.dtiblog.com/blog-entry-175.html を参照。
<!DOCTYPE html> <!-- IE9 でも表示できるようドキュメントタイプは HTML5 とする -->

<html>
<head>
<title>Test2</title>
<style>
table {
border-collapse:collapse;
}
td {
vertical-align: top;
border-bottom: 1px solid gray;
border-top: 1px solid gray;
padding: 0px;
}
.prop {
color: red;
font-family: 'Times New Roman';
}
.val {
vertical-align: middle;
font-family: 'Courier New', monospace;
}
div {
padding-top: 20px;
padding-bottom: 10px;
font-size: 150%;
font-family: 'Times New Roman';
font-weight: bold;
}
</style>
</head>
<body>
<script>
var SVG_NS = 'http://www.w3.org/2000/svg';

function print_props(obj) {
var props = ['nodeName', 'namespaceURI', 'tagName', 'prefix', 'localName'];
for (var i = 0; i < props.length; i++ ) {
document.write('<tr><td class="prop">');
document.write(props[i]);
document.write('&nbsp;&nbsp;&nbsp;&nbsp;</td><td class="val">');
document.write(obj[props[i]]);
document.write('</td></tr>');
}
}

function print_elem(el) {
document.write('<table><tbody>');
print_props(el);
print_attrs(el);
document.write('</tbody></table>');
}

function print_attrs(el) {
for(var i = 0 ; i < el.attributes.length; i++) {
var attr = el.attributes[i];
document.write('<tr><td class="prop">attributes[' + i + ']:</td>');
document.write('<td>');
document.write('<table><tbody>');
print_props(attr);
document.write('</tbody></table>');
document.write('</td><tr>');
}
}


var root = document.createElementNS(SVG_NS, 'svg:svg');
// rect要素は接頭辞を省略して作成する.
var el1 = document.createElementNS(SVG_NS, 'rect');
root.appendChild(el1);

text = (new XMLSerializer()).serializeToString(root);
text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');

document.write('<div><i>svg</i> node detail</div>');
print_elem(root);

document.write('<div><i>rect</i> node detail</div>');
print_elem(el1);

document.write('<div>result of serializeToString() method.</div>');
document.write('<span class="val">');
document.write(text);
document.write('</span>');

</script>
</body>
</html>

Firefoxでの実行結果
以下を見ると rect ノードオブジェクトの prefix が null であるにも関わらず、
シリアライズされた文字列に svg:rect と接頭辞が追加されていることがわかる。
Firefoxでの実行結果

次は、名前空間宣言を明示的に追加し、接頭辞もすべて指定するサンプルコードと実行結果。強調表示部が上のコードに追加した箇所。
<!DOCTYPE html> <!-- IE9 でも表示できるようドキュメントタイプは HTML5 とする -->

<html>
<head>
<title>Test3</title>
<style>
table {
border-collapse:collapse;
}
td {
vertical-align: top;
border-bottom: 1px solid gray;
border-top: 1px solid gray;
padding: 0px;
}
.prop {
color: red;
font-family: 'Times New Roman';
}
.val {
vertical-align: middle;
font-family: 'Courier New', monospace;
}
div {
padding-top: 20px;
padding-bottom: 10px;
font-size: 150%;
font-family: 'Times New Roman';
font-weight: bold;
}
</style>
</head>
<body>
<script>
var XMLNS_NS = 'http://www.w3.org/2000/xmlns/';
var SVG_NS = 'http://www.w3.org/2000/svg';

function print_props(obj) {
var props = ['nodeName', 'namespaceURI', 'tagName', 'prefix', 'localName'];
for (var i = 0; i < props.length; i++ ) {
document.write('<tr><td class="prop">');
document.write(props[i]);
document.write('&nbsp;&nbsp;&nbsp;&nbsp;</td><td class="val">');
document.write(obj[props[i]]);
document.write('</td></tr>');
}
}

function print_elem(el) {
document.write('<table><tbody>');
print_props(el);
print_attrs(el);
document.write('</tbody></table>');
}

function print_attrs(el) {
for(var i = 0 ; i < el.attributes.length; i++) {
var attr = el.attributes[i];
document.write('<tr><td class="prop">attributes[' + i + ']:</td>');
document.write('<td>');
document.write('<table><tbody>');
print_props(attr);
document.write('</tbody></table>');
document.write('</td><tr>');
}
}


var root = document.createElementNS(SVG_NS, 'svg:svg');
var el1 = document.createElementNS(SVG_NS, 'svg:rect');
root.appendChild(el1);
root.setAttributeNS(XMLNS_NS, 'xmlns:svg', SVG_NS);

text = (new XMLSerializer()).serializeToString(root);
text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');

document.write('<div><i>svg</i> node detail</div>');
print_elem(root);

document.write('<div><i>rect</i> node detail</div>');
print_elem(el1);

document.write('<div>result of serializeToString() method.</div>');
document.write('<span class="val">');
document.write(text);
document.write('</span>');

</script>
</body>
</html>

Firefoxでの実行結果
Firefoxでの実行結果2

[Web開発] createElementNS 修飾名(qualified name) プレフィックス 接頭辞を省略したらどうなるか。

 
createElementNS の呼び出しで、修飾名(qualified name)の名前空間プレフィックスを省略した場合、どうなるかを検証した。
出力結果は、実装(おそらくはシリアライザの実装)によってまちまち。

Javaでの検証
検証環境のバージョンは以下のとおり。
Java : 1.7.0_02
Xerces : 2.11.0

以下のテストコードは簡易版。
JAXP の Transformer や DOM Level 3 Load and Save API のシリアライザを含めたより詳細な検証は、http://monado.dtiblog.com/blog-entry-177.htmlで実施。

NSTest.java
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.apache.xml.serialize.OutputFormat;
import org.apache.xml.serialize.XMLSerializer;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class NSTest {

    static final String SVG_NS = "http://www.w3.org/2000/svg";

    public static void main(String[] argv) throws Exception {
        DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance();
        dbfactory.setNamespaceAware(true);
        DocumentBuilder builder = dbfactory.newDocumentBuilder();
        Document doc = builder.newDocument();

        // 要素rootを作成する
        Element root = doc.createElementNS(SVG_NS, "svg:svg");
        // 要素 el1 は名前空間プレフィックスを指定せずに作成する
        Element el1 = doc.createElementNS(SVG_NS, "rect");
        root.appendChild(el1);

        // 要素をシリアライズする
        OutputFormat format = new OutputFormat();
        format.setOmitXMLDeclaration(true);
        format.setIndenting(true);
        XMLSerializer serializer = new XMLSerializer(System.out, format);
        serializer.serialize(root);
    }
}


NSTest.java の実行結果
<svg:svg>
    <rect/>
</svg:svg>


JavaScript での検証
Internet Explorer では、createElementNS がバージョン 9 から利用可能であり、
InternetExplorer9 で createElementNS を使うために文書タイプをHTML5としている。
なお、http://monado.dtiblog.com/blog-entry-176.html にあるテストコードを使用すると、シリアライザがタグ名のプレフィックスを調整しているのが確認できる。
以下のコードは、簡単にするためそれをしていない。

<!DOCTYPE html> <!-- IE9 でも表示できるようドキュメントタイプは HTML5 とする -->
<html>
<head>
<title>Test1</title>
</head>
<body>
  <script>
    var SVG_NS = 'http://www.w3.org/2000/svg';
    var root = document.createElementNS(SVG_NS, 'svg:svg');
    // rect要素は接頭辞を省略して作成する.
    var el1 = document.createElementNS(SVG_NS, 'rect');
    root.appendChild(el1);

    text = (new XMLSerializer()).serializeToString(root);
    text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    document.write(text);

  </script>
</body>
</html>

各ブラウザの実行結果は以下の通り。

Internet Explorer 9.0.8112.16421
<svg:svg xmlns:svg="http://www.w3.org/2000/svg"><rect xmlns="http://www.w3.org/2000/svg" /></svg:svg>

Firefox 9.0.1
<svg:svg xmlns:svg="http://www.w3.org/2000/svg"><svg:rect/></svg:svg>
Firefox では、rect要素の省略された接頭辞が nsXMLContentSerializer::ConfirmPrefix メンバ関数によって算出される。これは、Firefox のソースコード nsXMLContentSerializer.cpp で確認できる。

Google Chrome 16.0.912.77
<svg:svg><rect></rect></svg:svg>


補足
下記のコードを書いてみたが Google Chrome では期待通りに動作しなかった。
調べたところ、Chrome のレンダリングエンジン Webkit では、 XMLSerializerserializeToString が正しく実装されていないようだ。
不具合については次のリンクを参照。

xmlserializer strips xlink from xlink:html svg image tag
http://stackoverflow.com/questions/8979267/xmlserializer-strips-xlink-from-xlinkhtml-svg-image-tag

<!DOCTYPE html> <!-- IE9 でも表示できるようドキュメントタイプは HTML5 とする -->
<html>
<head>
<title>Test setAttributeNS</title>
</head>
<body>
  <script>
    var XMLNS_NS = 'http://www.w3.org/2000/xmlns/';
    var SVG_NS = 'http://www.w3.org/2000/svg';

    var root = document.createElementNS(SVG_NS, 'svg:svg');
    root.setAttributeNS(XMLNS_NS, 'xmlns:svg', SVG_NS);
    var el1 = document.createElementNS(SVG_NS, 'svg:rect');
    root.appendChild(el1);

    text = (new XMLSerializer()).serializeToString(root);
    text = text.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
    document.write(text);

  </script>
</body>
</html>

[Web開発] userData ビヘイビア behavior 動かない 保存できない 機能しない

 
UserData ビヘイビアが動作しない場合には、適用されているドキュメントモードを確認してみると良い。
ドキュメントモードについては以下を参照のこと。

標準モードと互換モードについて
http://www.dspt.net/html_tag/mode.html

ドキュメントモードに対する UserData ビヘイビアの動作状況は下表の通り。
下表は、IE9で検証した結果である。
ドキュメントモード document.compatMode document.documentMode 動作状況
互換モード"BackCompat"5動作する
標準モード(IE7)"CSS1Compat"7動作する
標準モード(IE8)"CSS1Compat"8動作する
標準モード(IE9)"CSS1Compat"9動作しない

UserData ビヘイビアを動作させるためには、さらに、対象のセキュリティゾーンの設定の「UserDataの常設」を有効にする必要がある。
対象のセキュリティ・ゾーンがマイコンピュータ・ゾーンである場合には、レジストリの編集が必要になることもある。
マイコンピュータ・ゾーンの設定は、ファイルプロトコル(file://)によるローカルファイルへのアクセスなどに適用される。

動作確認用HTMLソース(IE用)
<html>

<head>
<!-- 次の meta タグによってレンダリングモードを指定 -->
<meta http-equiv="X-UA-Compatible" content="IE=5" />
<!-- content "IE=5":互換モード, "IE=7":標準モード(IE7), "IE=8":標準モード(IE8), "IE=9":標準モード(IE9) -->
<script>

function DoSave() {
form1.data.setAttribute("content", form1.textbox.value);
form1.data.save("EditContent");
}

function DoLoad() {
form1.data.load("EditContent");
content = form1.data.getAttribute("content");
form1.textbox.value = content || "default";
}

function DoClear() {
form1.textbox.value = "";
}

</script>
</head>
<body onload="DoLoad();">
<pre>
<script>
if(document.compatMode)
document.writeln("document.compatMode:" + document.compatMode);
if(document.documentMode)
document.writeln("document.documentMode:" + document.documentMode);
</script>
</pre>
<form name="form1">
<input id="data" type="hidden" style="behavior:url(#default#userdata)" />
<input id="textbox" type="text"/>
<p>
<button onClick='DoSave()'>Save</button>
<button onClick='DoClear();'>Clear</button>
<button onClick='DoLoad()'>Load</button>
</p>
</form>
</body>
</html>