การทำให้ XML เป็นมาตรฐานมักใช้เมื่อมีความจำเป็นต้องสร้างลายเซ็นดิจิทัลเพื่อส่งให้คู่ค้าตรวจสอบ เนื่องจากลายเซ็นดิจิทัลสร้างขึ้นจากข้อมูล XML ข้อมูล XML จึงต้องถูกทำให้เป็นมาตรฐานก่อนจึงจะสามารถคำนวณค่าลายเซ็นได้ แม้แต่ช่องว่างพิเศษก็อาจส่งผลต่อค่าลายเซ็นที่คำนวณได้ ดังนั้นจึงต้องปฏิบัติตามกฎบางอย่างเพื่อทำให้ข้อมูล XML เป็นมาตรฐานเพื่อให้มีรูปแบบมาตรฐาน นี่คือเหตุผลที่ W3C สร้างข้อกำหนด Canonical XML Version 1.1
ข้อกำหนดนี้มีกฎในการจัดรูปแบบโหนดองค์ประกอบ โหนดแอตทริบิวต์ และโหนดเนมสเปซ ฯลฯ ภาษาโปรแกรมต่างๆ ได้นำข้อกำหนดนี้ไปใช้เพื่อให้เราสามารถใช้เพื่อทำให้ข้อมูล XML เป็นมาตรฐานได้อย่างง่ายดายโดยไม่ต้องทราบรายละเอียดเกี่ยวกับข้อกำหนด
ในบทช่วยสอนนี้ เราจะไม่ลงรายละเอียดในข้อกำหนด เราจะเน้นเฉพาะวิธีการเรียก Java API เพื่อทำให้ข้อมูล XML เป็นมาตรฐานเท่านั้น ตาม XPath data model เอกสาร XML แสดงด้วยชุดของโหนด -- ชุดโหนด ในขณะที่ XML API ใน Java รับออบเจ็กต์ Data ที่จะทำให้เป็นมาตรฐาน เพื่อสาธิตการทำให้เป็นมาตรฐาน เราจะสร้าง NodeSetDataImpl ที่กำหนดเองซึ่งใช้ NodeSetData และอินเทอร์เฟซ Iterator
import java.util.Iterator;
import javax.xml.crypto.NodeSetData;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.traversal.DocumentTraversal;
import org.w3c.dom.traversal.NodeFilter;
import org.w3c.dom.traversal.NodeIterator;
public class NodeSetDataImpl implements NodeSetData, Iterator {
private Node ivNode;
private NodeFilter ivNodeFilter;
private Document ivDocument;
private DocumentTraversal ivDocumentTraversal;
private NodeIterator ivNodeIterator;
private Node ivNextNode;
public NodeSetDataImpl(Node pNode, NodeFilter pNodeFilter) throws Exception {
ivNode = pNode;
ivNodeFilter = pNodeFilter;
if (ivNode instanceof Document) {
ivDocument = (Document) ivNode;
} else {
ivDocument = ivNode.getOwnerDocument();
}
ivDocumentTraversal = (DocumentTraversal) ivDocument;
}
private NodeSetDataImpl(NodeIterator pNodeIterator) {
ivNodeIterator = pNodeIterator;
}
public Iterator iterator() {
NodeIterator nodeIterator = ivDocumentTraversal.createNodeIterator(ivNode, NodeFilter.SHOW_ALL, ivNodeFilter, false);
return new NodeSetDataImpl(nodeIterator);
}
private Node checkNextNode() {
if (ivNextNode == null && ivNodeIterator != null) {
ivNextNode = ivNodeIterator.nextNode();
if (ivNextNode == null) {
ivNodeIterator.detach();
ivNodeIterator = null;
}
}
return ivNextNode;
}
private Node consumeNextNode() {
Node nextNode = checkNextNode();
ivNextNode = null;
return nextNode;
}
public boolean hasNext() {
return checkNextNode() != null;
}
public Node next() {
return consumeNextNode();
}
public void remove() {
throw new UnsupportedOperationException("Removing nodes is not supported.");
}
public static NodeFilter getRootNodeFilter() {
return new NodeFilter() {
public short acceptNode(Node pNode) {
if (pNode instanceof Element && pNode.getParentNode() instanceof Document) {
return NodeFilter.FILTER_SKIP;
}
return NodeFilter.FILTER_ACCEPT;
}
};
}
}
จากนั้นเราต้องสร้าง CanonicalizationMethod เพื่อดำเนินการทำให้เป็นมาตรฐานจริง มีตัวเลือกสองสามตัวที่นี่:
CanonicalizationMethod.INCLUSIVE CanonicalizationMethod.INCLUSIVE_WITH_COMMENTS CanonicalizationMethod.EXCLUSIVE CanonicalizationMethod.EXCLUSIVE_WITH_COMMENTS
ในบทช่วยสอนนี้ เราจะสาธิตเฉพาะการทำให้เป็นมาตรฐานแบบรวมเท่านั้น ด้านล่างนี้คือโค้ดเพื่อทำสิ่งนี้
import java.io.ByteArrayInputStream;
import javax.xml.crypto.Data;
import javax.xml.crypto.OctetStreamData;
import javax.xml.crypto.dsig.CanonicalizationMethod;
import javax.xml.crypto.dsig.XMLSignatureFactory;
import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
public class CanonicalizationTest {
private static String INPUT =
"" +
"hello" +
"world" +
"";
public static void main(String[] args){
transform(INPUT);
}
/**
* Create new document with data
*
* @param xml
* @return
*/
private static Document createNewDocument(String xml){
try{
byte[] bytes = xml.getBytes("UTF-8");
ByteArrayInputStream bin = new ByteArrayInputStream(bytes);
DocumentBuilderFactory fac = DocumentBuilderFactory.newInstance();
fac.setNamespaceAware(true);
DocumentBuilder docBuilder = fac.newDocumentBuilder();
Document doc = docBuilder.parse(bin);
return doc;
} catch (Exception ex){
ex.printStackTrace();
}
return null;
}
/**
* Transform a XML string
*
* @param xml
*/
private static void transform(String xml){
Document doc = createNewDocument(xml);
try{
Data data = new NodeSetDataImpl(doc, NodeSetDataImpl.getRootNodeFilter());
XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM");
CanonicalizationMethod canonicalizationMethod = fac.newCanonicalizationMethod(CanonicalizationMethod.INCLUSIVE, (C14NMethodParameterSpec)null);
// Doing the actual canonicalization
OctetStreamData transformedData =(OctetStreamData) canonicalizationMethod.transform(data, null);
byte[] bytes = tool.Util.readStream(transformedData.getOctetStream());
String str = new String(bytes);
System.out.println(str);
} catch (Exception ex){
ex.printStackTrace();
}
}
}
มาดูผลลัพธ์กันก่อน:
<my:Node xmlns:my="http://example.com" Id="1">hello</my:Node><my:Node xmlns:my="http://example.com" Id="2">world</my:Node>
การทดสอบจะสร้าง Document ก่อน จากนั้น Document นี้จะถูกส่งไปยัง NodeSetDataImpl เพื่อสร้างชุดโหนดที่จะทำให้เป็นมาตรฐาน จากเอาต์พุต เราจะเห็นว่าโหนด Root ถูกลบออกในข้อมูลที่ทำให้เป็นมาตรฐาน นี่เป็นเพราะ NodeFilter ใน NodeSetDataImpl ได้กรองโหนดนี้ออกไป ถัดไป my:Node ที่สองมีโหนด xmlns:my ก่อนโหนด Id ในรูปแบบที่ทำให้เป็นมาตรฐาน นี่เป็นไปตามข้อกำหนด Canonical XML ที่โหนดควรอยู่ในลำดับตามตัวอักษร
สำหรับวิธีการทำให้เป็นมาตรฐานอื่นๆ ตรรกะก็เหมือนกันที่นี่ ความแตกต่างคือเอาต์พุตไม่เหมือนกันเนื่องจากวิธีการทำให้เป็นมาตรฐานที่แตกต่างกันมีกฎที่แตกต่างกัน
No comment for this article.