首页 > 代码库 > Java学习之Xml系列三:dtd校验、改、增、删

Java学习之Xml系列三:dtd校验、改、增、删

见摘要、见代码注释,其他话不多说:

DTD文档:

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT SwordLibrary (Sword*)>
<!ELEMENT Sword (SwordName,Price,Attack)>
<!ELEMENT SwordName (#PCDATA)>
<!ELEMENT Price (#PCDATA)>
<!ELEMENT Attack (#PCDATA)>
<!ATTLIST Sword sno CDATA #REQUIRED>
<!ATTLIST Price type CDATA #IMPLIED>
<!ATTLIST Attack factor CDATA "1.0">

Xml文档:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE SwordLibrary SYSTEM "SwordTypeDefinition.dtd">
<SwordLibrary>
<Sword sno="s1">
<SwordName>欢欣之刃</SwordName>
<Price>1000</Price>
<Attack factor="1.0">10</Attack>
</Sword>
<Sword sno="s2">
<SwordName>夜叉</SwordName>
<Price>2050</Price>
<Attack factor="2.0">30</Attack>
</Sword>
<Sword sno="s3">
<SwordName>魔棒</SwordName>
<Price type="Dollar">200</Price>
<Attack factor="1.0">0</Attack>
</Sword>
</SwordLibrary>


java代码:

package JavaLeaner.XmlTest;

import java.io.FileOutputStream;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
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 org.junit.Test;
import org.w3c.dom.Document;
import org.w3c.dom.DocumentType;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;


public class XmlDemo3 {

    /*
     * 强制执行dtd校验,但是是在运行时
     * 
     */
    @Test
    public void Test1() throws ParserConfigurationException, SAXException, IOException
    {
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //强制执行dtd校验,但是是在运行时
        factory.setValidating(true);
        DocumentBuilder docDuilder = factory.newDocumentBuilder();
        Document doc = docDuilder.parse("src/JavaLeaner/XmlTest/SwordLib.xml");
        /*
         * 使用DocumentBuilderFactory的setValidating(true)方法可以将dtd校验在运行阶段执行
         * 可以正常编译,但在会爬出异常:(异常被声明,所以JUnit的运行条颜色是绿色)
         * Warning: validation was turned on but an org.xml.sax.ErrorHandler was not
set, which is probably not what is desired.  Parser will use a default
ErrorHandler to print the first 10 errors.  Please call
the ‘setErrorHandler‘ method to fix this.
Error: URI=file:///home/XXXX/workspace/Learning/HelloWorld/src/JavaLeaner/XmlTest/SwordLib.xml Line=9: 需要属性 "sno", 并且必须为元素类型 "Sword" 指定该属性。
         */
        //单步执行抛出异常的语句在docDuilder.parse
    }
    
    /*
     * 改:
     * 修改修改已有标签内容和属性
     * 注意:属性如果有则是修改,无则会添加,使用的都是setAttribute
     */
    @Test
    public void Test2() throws ParserConfigurationException, SAXException, IOException, TransformerException 
    {
        String fileName="src/JavaLeaner/XmlTest/SwordLib.xml";
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        //factory.setValidating(true);
        DocumentBuilder docDuilder = factory.newDocumentBuilder();
        Document doc = docDuilder.parse(fileName);
            
        //Element rootElement = doc.getDocumentElement();
        Element rootElement = (Element)doc.getElementsByTagName("SwordLibrary").item(0);
        NodeList list= rootElement.getElementsByTagName("Price");//由此可以看出,这里的搜索不仅仅是子元素,也可以是孙子元素
        for(int i=0;i<list.getLength();i++)
        {
            Element e = (Element)list.item(i);
            //更改或者添加属性设置
            e.setAttribute("type", "yan");
            //更改元素内容
            e.setTextContent("0");
        }
        
        //将更改后的文档对象写回xml文件
        TransformerFactory transformerfactory = TransformerFactory.newInstance();
        Transformer tf=transformerfactory.newTransformer();
        //tf.transform(new DOMSource(doc.getDoctype()),new StreamResult(new FileOutputStream(fileName)));
        
        //注意:如果想让xml中的Doctype标签不丢失,必须在转换类对象进行输出属性设置。
        //下面这句由于对xml的Dctype设置的dtd声明为public的情况
        //tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_PUBLIC, doc.getDoctype().getPublicId());    
        //下面这句由于对xml的Dctype设置的dtd声明为System的情况
        tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); 
        
        tf.transform(new DOMSource(doc),new StreamResult(new FileOutputStream(fileName)));

/*        <?xml version="1.0" encoding="UTF-8" standalone="no"?><SwordLibrary>
        <Sword sno="s1">
        <SwordName>欢欣之刃</SwordName>
        <Price type="yan">0</Price>
        <Attack factor="1.0">10</Attack>
        </Sword>
        <Sword sno="s2">
        <SwordName>夜叉</SwordName>
        <Price type="yan">0</Price>
        <Attack factor="2.0">30</Attack>
        </Sword>
        <Sword sno="s3">
        <SwordName>魔棒</SwordName>
        <Price type="yan">0</Price>
        <Attack factor="1.0">0</Attack>
        </Sword>
        </SwordLibrary>
        */
    }
    
    /*
     * 
     * 追加节点
     *
     */
    @Test
    public void Test3() throws ParserConfigurationException, SAXException, IOException, TransformerException 
    {
        String fileName="src/JavaLeaner/XmlTest/SwordLib.xml";
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        DocumentBuilder docDuilder = factory.newDocumentBuilder();
        Document doc = docDuilder.parse(fileName);
        
        //用文档对象创建元素
        Element eSword=doc.createElement("Sword");
        eSword.setAttribute("sno", "s4");
        
        //用文档对象创建子元素
        Element esname=doc.createElement("SwordName");
        Element esprice=doc.createElement("Price");
        Element esattack=doc.createElement("Attack");
        
        //发现问题后,补上的
        esname.setTextContent("散华");
        esprice.setTextContent("2050");
        esattack.setTextContent("30");
        
        //将子元素添加到元素里
        eSword.appendChild(esname);
        eSword.appendChild(esprice);
        eSword.appendChild(esattack);
        //将元素添加到文档里,及根元素
        doc.getDocumentElement().appendChild(eSword);
        //注意:        doc.appendChild(eSword);是错误的
        //eSword应该在根元素SwordLibrary下
        System.out.println("doc.getDocumentElement():  "+doc.getDocumentElement().getNodeName());

        
        
        //将更改后的文档对象写回xml文件
        TransformerFactory transformerfactory = TransformerFactory.newInstance();
        Transformer tf=transformerfactory.newTransformer();
        
        //注意:如果想让xml中的Doctype标签不丢失,必须在转换类对象进行输出属性设置。
        //下面这句由于对xml的Dctype设置的dtd声明为public的情况
        //tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_PUBLIC, doc.getDoctype().getPublicId());    
        //下面这句由于对xml的Dctype设置的dtd声明为System的情况
        tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); 

        
        tf.transform(new DOMSource(doc),new StreamResult(new FileOutputStream(fileName)));
        
        /*
         * 
         * 
         * 
         * Warning: validation was turned on but an org.xml.sax.ErrorHandler was
         * not set, which is probably not what is desired. Parser will use a
         * default ErrorHandler to print the first 10 errors. Please call the
         * ‘setErrorHandler‘ method to fix this. Error:
         * URI=file:///home/neil/workspace
         * /Learning/HelloWorld/src/JavaLeaner/XmlTest/SwordLib.xml Line=1:
         * 文档根元素 "SwordLibrary" 必须匹配 DOCTYPE 根 "null"。 Error:
         * URI=file:///home/neil
         * /workspace/Learning/HelloWorld/src/JavaLeaner/XmlTest/SwordLib.xml
         * Line=1: 文档无效: 找不到语法。 doc.getDocumentElement(): SwordLibrary
         * 
         * 
         * <?xml version="1.0" encoding="UTF-8" standalone="no"?><SwordLibrary>
         * <Sword sno="s1"> <SwordName>欢欣之刃</SwordName> <Price>1000</Price>
         * <Attack factor="1.0">10</Attack> </Sword> <Sword sno="s2">
         * <SwordName>夜叉</SwordName> <Price>2050</Price> <Attack
         * factor="2.0">30</Attack> </Sword> <Sword sno="s3">
         * <SwordName>魔棒</SwordName> <Price type="Dollar">200</Price> <Attack
         * factor="1.0">0</Attack> </Sword> <Sword
         * sno="s4"><SwordName/><Price/><Attack/></Sword></SwordLibrary>
         */


        
        
    }
    
    
    /*
     * 实验
     * 在之前的实验中发现,虽然xml被成功更改,但是Doctype属性在文档对象写回文件后发生了丢失的现象
     * 这是因为DocType写回xml文件时,需要单独设置属性setOutputProperty
     */
    @Test
    public void Test4() throws ParserConfigurationException, SAXException, IOException, TransformerException
    {

        
        {
            String fileName="src/JavaLeaner/XmlTest/SwordLib.xml";
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            //factory.setValidating(true);
            DocumentBuilder docDuilder = factory.newDocumentBuilder();
            Document doc = docDuilder.parse(fileName);
                
/*            DocumentType doctype = doc.getDoctype();
            String docText = doctype.getName();
            System.out.println("DocumentType:" + docText);*/
            
            
            
            TransformerFactory transformerfactory = TransformerFactory.newInstance();
            Transformer tf=transformerfactory.newTransformer();
            
            if(doc==null)
            {
                System.out.println("doc is null");    
                return;
            }
            if(doc.getDoctype()==null)
            {
                System.out.println("DocumentType is null");    
                return;
            }
            
            //注意:如果想让xml中的Doctype标签不丢失,必须在转换类对象进行输出属性设置。
            //下面这句由于对xml的Dctype设置的dtd声明为public的情况
            //tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_PUBLIC, doc.getDoctype().getPublicId());    
            //下面这句由于对xml的Dctype设置的dtd声明为System的情况
            tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); 
            
            //doc.setXmlStandalone(xmlStandalone);
            DOMSource domSource=new DOMSource(doc);
            tf.transform(domSource,new StreamResult(new FileOutputStream(fileName)));
        
        

/*            DocumentType doctype2 = doc.getDoctype();
            if (doctype2 != null) {
                String docText2 = doctype2.getName();
                System.out.println("DocumentType:" + docText2);
            } else {
                System.out.println("DocumentType is null");            

            }*/
        
        }
        
        
        
        {
            String fileName="src/JavaLeaner/XmlTest/SwordLib.xml";
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            //factory.setValidating(true);
            DocumentBuilder docDuilder = factory.newDocumentBuilder();
            Document doc = docDuilder.parse(fileName);
            System.out.println("getXmlStandalone:" + (doc.getXmlStandalone()?1:0));
            DocumentType doctype = doc.getDoctype();
            if (doctype != null) {
                String docText = doctype.getName();
                System.out.println("DocumentType:" + docText);
            } else {
                System.out.println("DocumentType is null");            

            }
        }
        
    }
    
    
    /*
     * 删除节点:方法1://从爸爸找起,逐一审查儿子
     */
    @Test
    public void Test5() throws ParserConfigurationException, SAXException, IOException, TransformerException
    {
        String fileName="src/JavaLeaner/XmlTest/SwordLib.xml";
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        DocumentBuilder docDuilder = factory.newDocumentBuilder();
        Document doc = docDuilder.parse(fileName);
        
        //从爸爸找起,逐一审查儿子
        NodeList list = doc.getElementsByTagName("Sword");
        for (int i = 0; i < list.getLength(); i++) {
            Element eSword = (Element) list.item(i);
            if (eSword.getElementsByTagName("SwordName").getLength() >= 0) {
                Element ename = (Element) eSword.getElementsByTagName(
                        "SwordName").item(0);

                if ("散华".equals(ename.getTextContent())) {
                    ((Element)doc.getDocumentElement()).removeChild(eSword);
                    
                    System.out.println("deleted!");
                }
            }
        }
        
        //将更改后的文档对象写回xml文件
        TransformerFactory transformerfactory = TransformerFactory.newInstance();
        Transformer tf=transformerfactory.newTransformer();
        
        //如果想让xml中的Doctype标签不丢失,必须在转换类对象进行输出属性设置。
        tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); 

        
        tf.transform(new DOMSource(doc),new StreamResult(new FileOutputStream(fileName)));

    }
    
    /*
     * 删除节点:方法2://直接找当事人,然后爷爷打爸爸。(看不懂吗?看看代码就知道了)
     */
    @Test
    public void Test6() throws ParserConfigurationException, SAXException, IOException, TransformerException
    {
        String fileName="src/JavaLeaner/XmlTest/SwordLib.xml";
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setValidating(true);
        DocumentBuilder docDuilder = factory.newDocumentBuilder();
        Document doc = docDuilder.parse(fileName);
        
        NodeList list = doc.getElementsByTagName("SwordName");
        for (int i = 0; i < list.getLength(); i++) {
            Element ename = (Element) list.item(i);
            if("散华".equals(ename.getTextContent()))
            {
                //典型的爷爷打爸爸
                ename.getParentNode().getParentNode().removeChild(ename.getParentNode());
                System.out.println("deleted!");
            }
        }
        
        //将更改后的文档对象写回xml文件
        TransformerFactory transformerfactory = TransformerFactory.newInstance();
        Transformer tf=transformerfactory.newTransformer();
        
        //如果想让xml中的Doctype标签不丢失,必须在转换类对象进行输出属性设置。
        tf.setOutputProperty(javax.xml.transform.OutputKeys.DOCTYPE_SYSTEM, doc.getDoctype().getSystemId()); 

        
        tf.transform(new DOMSource(doc),new StreamResult(new FileOutputStream(fileName)));

    }
    
    
    
}