2016年11月13日 星期日

XPath

參考資料(英文版)
參考資料(簡中版)
XPath在javax.xml.xpath包裡
這篇的Document、Element、NodeList是import org.w3c.dom 這一包,不要import錯了

※test.xml

<root>
    <first fa="f1">
        <second sa="s1">a1</second>
        <second>
            <third>
                a2
                <fourth>
                    a3
                </fourth>
            </third>
            b1
        </second>
        <second>c1</second>
    </first>
    
    <first fa="f2">
        <second sa="s2">d1</second>
        <first fa="f2">
            e1
            <second sa="s3">
                e2
                <third>e3</third>
            </second>
        </first>
        <second>f1</second>
    </first>
    
    <first fa="f3">
        <second sa="s3">g1</second>
        <second>
            h1
            <first fa="f3">
                h2
                <third>
                    h3
                    <second>
                        h4
                    </second>
                </third>
            </first>
        </second>
        <second>i1</second>
    </first>
</root>

※測試用的xml


※Test.java

try (InputStream is = Test.class.getClassLoader().getResourceAsStream("test.xml")) {
    try {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = db.parse(is);
        XPath xpath = XPathFactory.newInstance().newXPath();
        NodeList list = (NodeList) xpath.evaluate("//first[@fa='f1']", doc, XPathConstants.NODESET);
    
        for (int i = 0; i < list.getLength(); i++) {
            Element element = (Element) list.item(i);
            NodeList list2 = element.getElementsByTagName("second");
            for (int j = 0; j < list2.getLength(); j++) {
                // System.out.println("node name=" + list2.item(j).getNodeName());
                System.out.println("text content=" + list2.item(j).getTextContent());
                
                NamedNodeMap map = list2.item(j).getAttributes();
                System.out.println("attr=>" + map.getNamedItem("sa"));
                System.out.println("==========");
            }
        }
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    } catch (XPathExpressionException e) {
        e.printStackTrace();
    }
} catch (IOException e1) {
    e1.printStackTrace();
}

※XPathConstants.NODESET表示回傳什麼型態,有5種
NUMBER、STRING、BOOLEAN這3個就不用說了
NODESET:回傳0~多個結點
NODE:回傳0~1個結點
下面還會說明兩者的差異

※「//first[@fa='f1']」的結果為:
text content=a1
attr=>sa="s1"
==========
text content=

a2

a3


b1

attr=>null
==========
text content=c1
attr=>null
==========

second有3個,所以會跑3次,可以看到第2個second以下的(包括子元素)都會跑,甚至連換行都有


※「//first[@fa='f2']」的結果為:
text content=d1
attr=>sa="s2"
==========
text content=
e2
e3

attr=>sa="s3"
==========
text content=f1
attr=>null
==========
text content=
e2
e3

attr=>sa="s3"
==========

因為first屬性是f2的有兩個,所以會跑4次


※「//first[@fa='f3']」的結果為:
text content=g1
attr=>sa="s3"
==========
text content=
h1

h2

h3

h4




attr=>null
==========
text content=
h4

attr=>null
==========
text content=i1
attr=>null
==========
text content=
h4

attr=>null
==========

first屬性是f3有兩個,第一次跑second有4個
然後第二次又再跑一次,所以是5次


※「//」和「/」的差別

//:不管任何層級,全部都會找
/:只會從根目錄開始找,以上面的例子,就是root

以「//first[@fa='f2']」為例,改成「/first[@fa='f2']」什麼結果都沒有,因為根目錄是root
再改成「/root/first[@fa='f2']」可以找到,但因為是從根目錄開始找,所以結果只會跑3次,少了子元素是first且屬性是fa,值是f2的那一次

※以上面的例子「/root/first」 可以發現NodeList長度為3,也就是全部都抓,如果只想抓第二筆,可以用「/root/first[2]」,要注意是從1開始的


※XPathConstants.NODESET/NODE差異

※「/」也可以用Node,就是上面程式碼的XPathConstants.NODESET,改成XPathConstants.NODE,如下:
try (InputStream is = TestJunit.class.getClassLoader().getResourceAsStream("xml.xml")) {
    try {
        DocumentBuilder db = DocumentBuilderFactory.newInstance().newDocumentBuilder();
        Document doc = db.parse(is);
        XPath xpath = XPathFactory.newInstance().newXPath();
        Element element = (Element) xpath.evaluate("//first[@fa='f2']", doc, XPathConstants.NODE);
        NodeList list2 = element.getElementsByTagName("second");

        System.out.println(list2.getLength());
        for (int j = 0; j < list2.getLength(); j++) {
            System.out.println("text content=" + list2.item(j).getTextContent());
    
            NamedNodeMap map = list2.item(j).getAttributes();
            System.out.println("attr=>" + map.getNamedItem("sa"));
            System.out.println("==========");
        }
    } catch (ParserConfigurationException e) {
        e.printStackTrace();
    } catch (SAXException e) {
        e.printStackTrace();
    } catch (XPathExpressionException e) {
        e.printStackTrace();
    }
} catch (IOException e1) {
    e1.printStackTrace();
}

※Node強轉成Element;Node強轉成NodeList

2016年11月11日 星期五

XMLInputFactory

※XMLInputFactory


※xml.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
    <first fa="f1">
        <second1 sa="s1">aaa</second1>
        <second2>bbb</second2>
        <second3>ccc</second3>
    </first>
    
    <first fa="f2">
        <second1 sa="s2">ddd</second1>
        <second2>eee</second2>
        <second3>fff</second3>
    </first>
    
    <first fa="f3">
        <second1 sa="s3">ggg</second1>
        <second2>hhh</second2>
        <second3>iii</second3>
    </first>
</root>

※測試用的xml


※循序讀取

public void readXMLSequence() throws XMLStreamException {
    XMLInputFactory factory = XMLInputFactory.newInstance();
    try (InputStream is = Test.class.getClassLoader().getResourceAsStream("xml.xml")) {
        XMLStreamReader reader = null;
        reader = factory.createXMLStreamReader(is);
        while (reader.hasNext()) {
            int type = reader.next();
            System.out.println("type=" + type);
    
            if (type == XMLStreamConstants.START_ELEMENT) {
                System.out.println("<" + reader.getName() + ">");
            } else if (type == XMLStreamConstants.CHARACTERS) {
                System.out.println("tag之間=" + reader.getText().trim());
            } else if (type == XMLStreamConstants.END_ELEMENT) {
                System.out.println("</" + reader.getName() + ">");
            }
        }
        reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

※tag和tag之間也會讀取


※讀取屬性

public void readXMLAttribute() throws XMLStreamException {
    XMLInputFactory factory = XMLInputFactory.newInstance();
    try (InputStream is = Test.class.getClassLoader().getResourceAsStream("xml.xml")) {
        XMLStreamReader reader = null;
        reader = factory.createXMLStreamReader(is);
        while (reader.hasNext()) {
            int type = reader.next();
            if (type == XMLStreamConstants.START_ELEMENT) {
                String name = reader.getName().toString();
                if ("first".equals(name)) {
                    for (int i = 0; i < reader.getAttributeCount(); i++) {
                        System.out.println("attribute name=" + reader.getAttributeName(i));
                        System.out.println("attribute value=" + reader.getAttributeValue(i));
                    }
                }
            }
        }
        reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

※getResourceAsStream不熟的可看這篇


※讀取指定的元素

public void readXMLElement() throws XMLStreamException {
    XMLInputFactory factory = XMLInputFactory.newInstance();
    try (InputStream is = Test.class.getClassLoader().getResourceAsStream("xml.xml")) {
        XMLStreamReader reader = null;
        reader = factory.createXMLStreamReader(is);
    
        while (reader.hasNext()) {
            int type = reader.next();
            if (type == XMLStreamConstants.START_ELEMENT) {
                String name = reader.getName().toString();
                if ("second2".equals(name)) {
                    System.out.println("second2=" + reader.getElementText());
                }
    
                if ("second3".equals(name)) {
                    System.out.println("second3" + reader.getElementText() + System.getProperty("line.separator"));
                }
            }
        }
        reader.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}

※只抓元素是second2和second3


※讀取指定的元素2

public void readXMLElement2() throws XMLStreamException {
    XMLInputFactory factory = XMLInputFactory.newInstance();
    try (InputStream is = Test.class.getClassLoader().getResourceAsStream("xml.xml")) {
        XMLEventReader reader = factory.createXMLEventReader(is);
    
        int count = 0;
        while (reader.hasNext()) {
            XMLEvent event = reader.nextEvent();
            if (event.isStartElement()) {
                String name = event.asStartElement().getName().toString();
                if ("second2".equals(name)) {
                    System.out.println("second2=" + reader.getElementText());
                }
                if ("second3".equals(name)) {
                    System.out.println("second3" + reader.getElementText());
                }
            }
            count++;
        }
        System.out.println(count);
    } catch (IOException e) {
        e.printStackTrace();
    }
}

※使用XMLEvent


※使用過濾器

public void readXMLFilter() throws IOException {
    XMLInputFactory factory = XMLInputFactory.newInstance();
    try (InputStream is = Test.class.getClassLoader().getResourceAsStream("xml.xml")) {
        XMLEventReader reader = factory.createFilteredReader(factory.createXMLEventReader(is), new EventFilter() {
            @Override
            public boolean accept(XMLEvent event) {
                if (event.isStartElement()) {
                    String name = event.asStartElement().getName().toString();
                    if (Arrays.asList("second2", "second3").contains(name)) {
                        return true;
                    }
                }
                return false;
            }
        });
    
        int count = 0;
        while (reader.hasNext()) {
            XMLEvent event = reader.nextEvent();
            if (event.isStartElement()) {
                String name = event.asStartElement().getName().toString();
                if ("second2".equals(name)) {
                    System.out.println("second2=" + reader.getElementText());
                }
                if ("second3".equals(name)) {
                    System.out.println("second3" + reader.getElementText());
                }
            }
            count++;
        }
        System.out.println(count);
    } catch (XMLStreamException e) {
        e.printStackTrace();
    }
}

※

2016年11月7日 星期一

Class ClassLoader 的路徑問題

假設專案名稱是ProjectName
ProjectName主要要src和bin兩個資料夾
src放的是.java檔;bin放的是.class
當在src下增加.java和資料夾時,bin也會增加,但.java會變成.class
src下有個資料夾叫subPackage(bin當然也有)
以下程式碼是放在subPackage下執行的

※測試程式

System.out.println("a=" + TestJunit.class.getResource(""));//~ProjectName/bin/subPackage/
    
System.out.println("b=" + TestJunit.class.getResource("/"));//~ProjectName/bin/
    
System.out.println("c=" + TestJunit.class.getClassLoader().getResource(""));//~ProjectName/bin/

※class.getResource裡面的路徑可以分成用「/」和沒「/」開頭
「/」開頭的是src下的路徑(也是bin下的路徑);沒有「/」開頭的是目前路徑

※getClassLoader().getResource不能用「/」開頭,永遠都是null

※class.getResource("/")和getClassLoader().getResource("")是一樣的

※有兩個常用的方法getResource和getResourceAsStream,路徑取法都一樣
但getResource取不到是null;getResourceAsStream取不到會跳Exception

2016年11月6日 星期日

JAXB

JAXB全名為Java Architecture for XML Binding
是一種java和xml轉換的工具
以下以一個一對多的方式為例


※Employee.java

public class Employee {
    private String name;
    private int id;
    
    // setter/getter...
    
    public Employee(String name, int id) {
        super();
        this.name = name;
        this.id = id;
    }
    
    public Employee() {}
}

※預設建構子在java轉xml時會用到


※Dept.java

@XmlRootElement
public class Dept {
    private String name;
    private int did;
    private List<Employee> emps;
    
    // setter/getter...
    
    public Dept(String name, int did, List<Employee> emps) {
        super();
        this.name = name;
        this.did = did;
        this.emps = emps;
    }
    
    public Dept() {}
}

※預設建構子在xml轉java時會用到


※java轉xml

public void javaToXml() throws JAXBException {
    JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
    Marshaller ms = jaxb.createMarshaller();
    
    List<Employee> list = new ArrayList<>();
    list.add(new Employee("Bruce", 123));
    list.add(new Employee("Jacky", 456));
    list.add(new Employee("Mary", 789));
    
    ms.marshal(new Dept("information", 2, list), System.out);
    ms.marshal(new Dept("information", 2, list), new File("D:/jaxb.xml"));
}

※JAXBContext.newInstance裡面的類別必須要有@XmlRootElement

※屬性名為xml的標籤名,一對多的Employee叫emps,所以<emps>下會有它裡面的屬性<id>和<name>

※如果屬性是Map,那會變成
<屬性名><entry><key /><value /></entry>  <entry><key /><value /></entry></屬性名>


※xml轉java

public void xmlToJava() throws JAXBException {
    String line = System.getProperty("line.separator");
    String xml = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>" + line
            + "<dept><did>2</did><emps><id>123</id><name>Bruce</name></emps>" + line
            + "<emps><id>456</id><name>Jacky</name></emps>" + line + "<emps><id>789</id><name>Mary</name></emps>"
            + line + "<name>information</name></dept>";
    
    JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
    Unmarshaller um = jaxb.createUnmarshaller();
    
    // 使用String
    Dept dept1 = (Dept) um.unmarshal(new StringReader(xml));
    System.out.println(dept1.getName());
    System.out.println(dept1.getEmps().get(0).getName());
    System.out.println(dept1.getEmps().get(1).getName());
    System.out.println("==========");
    
    // 使用File
    Dept dept2 = (Dept) um.unmarshal(new File("D:/jaxb.xml"));
    System.out.println(dept2.getName());
    System.out.println(dept2.getEmps().get(0).getName());
    System.out.println(dept2.getEmps().get(1).getName());
}

※不管tag和tag之間有沒有空隔、tab、換行,我試的結果都是可以的



※@XmlRootElement、@XmlAccessorType、@XmlElement、@XmlTransient、@XmlType、@XmlAttribute、@XmlAccessorOrder

這幾個Annotation 都是在 javax.xml.bind.annotation套件裡,裡面有很多,但這幾個比較常用

※Dept.java

@XmlType(propOrder={"did", "name"})
@XmlRootElement(name = "root")
@XmlAccessorType(XmlAccessType.FIELD)
public class Dept {
    @XmlElement(name = "first")
    private String name;
    
    @XmlElement
    private int did;
    
    @XmlTransient
    private List<Employee> emps;
    
    // ...
}

※@XmlRootElement是一定要有的,預設name名稱就是class名稱,所以tag會變成Dept,如果想改可以自行定義,像我上面的例子改成root

※@XmlTransient:不產生xml

※@XmlAccessorType 有以下4種:
FIELD:產生的xml,以成員變數為名
PROPERTY:產生的xml,以setter/getter為名,setter/getter為一組,少一個叫不會產生xml
PUBLIC_MEMBER:此為預設值,以setter/getter為名,但還得是public
NONE:整個類別不產生xml

※@XmlElement是針對子元素的,如果想改名,不能為null…等,都可在這設定
要注意@XmlAccessorType的設定會影響非類別註解寫的位置
.如果@XmlAccessorType設為FIELD,那麼註解要設在field才有效

.如果@XmlAccessorType設為PROPERTY,那麼這個註解要設在getter才有效;設在setter會報錯

.如果@XmlAccessorType是預設的PUBLIC_MEMBER,不能設定@XmlElement,會報「類別有相同名稱~」的錯

.如果@XmlAccessorType設為NONE,設定@XmlElement還是有效

※@XmlType的propOrder裡面除了已經註解為@XmlTransient以外,通通都得把元素寫出來,否則會報錯,它的功能是將產生的xml,以這裡的設定為順序(不是@XmlElement裡的name)
.其他的屬性是針對Schema用的,在這裡看不出來有什麼不同

.@XmlAccessorOrder也是排序的,但只有兩個值
UNDEFINED:預設值,以java的順序為主(不是@XmlElement裡的name)
ALPHABETICAL:由a~z的順序為主(不是@XmlElement裡的name)
.兩個註解都下不會出錯,但為了必免混亂,最好只下一個

※@XmlAttribute為設定屬性的,如將did上面的註解改成這個,那did會變成Dept的屬性,當然也有name可以改變顯示的名字
.不能與@XmlElement同時使用,否則會產生「~含有互斥的註解~」的錯



※@XmlJavaTypeAdapter

為xml和java型態轉換用的,以下用一個日期轉換格式為例
使用這個annotation要寫一個class繼承XmlAdapter
我試過在Dept.java裡寫一個類別,不管是private、protected、default,都會出錯


※Dept.java

@XmlRootElement
public class Dept {
    @XmlJavaTypeAdapter(DateAdapter.class)
    private Date date;
    
    public Dept() {}
    
    public Dept(Date date) {
        this.date = date;
    }
}


※DateAdapter.java

public class DateAdapter extends XmlAdapter<String, Date> {
    DateFormat fmt = new SimpleDateFormat("yyyyMMdd HHmmss");
    
    @Override
    public Date unmarshal(String v) throws Exception {
        return fmt.parse(v);
    }
    
    @Override
    public String marshal(Date v) throws Exception {
        return fmt.format(v);
    }
}


※測試類

JAXBContext jaxb = JAXBContext.newInstance(Dept.class);
Marshaller ms = jaxb.createMarshaller();
jaxb.createMarshaller().marshal(new Dept(new Date()), System.out);



2016年11月5日 星期六

Schema elementFormDefault、attributeFormDefault

※elementFormDefault

※Test.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="!@#"
    xmlns:a="!@#" 
    elementFormDefault="qualified">
    
    <element name="root">
        <complexType>
            <sequence>
                <element name="q1" type="a:t1" />
                <element name="q2" type="a:t2" />
            </sequence>
        </complexType>
    </element>
    
    <complexType name="t1">
        <sequence>
            <element name="s1" type="string" />
            <element name="s2" type="a:t2" />
        </sequence>
    </complexType>
    
    <complexType name="t2">
        <sequence>
            <element name="s3" type="string" />
        </sequence>
    </complexType>
</schema>

※上一篇的xsd裡面都沒有使用xmlns,這裡因為自己也會用到,所以要加

※root裡有q1和q2,型態是自定的t1和t2,將root寫在t1和t2上面也是可以的

※這裡的意思是說,q1下有s1和s3;q2下有s3

※elementFormDefault有兩個值是qualified和unqualified(預設)
還有分全域和區域的element
全域的就是element="root"那一層,以下的element都是區域

qualified表示使用這裡的元素時,使不使用前綴都可以,但要用就全部都要用(包括全域)
unqualified表示使用這裡的元素時,區域一定不可使用前綴;全域一定要使用前綴

※Test.xml

<aa:root  
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# Test.xsd"
    xmlns:aa="!@#">
    
    <aa:q1>
        <aa:s1></aa:s1>
        <aa:s2>
            <aa:s3></aa:s3>
        </aa:s2>
    </aa:q1>
    <aa:q2>
        <aa:s3></aa:s3>
    </aa:q2>
</aa:root>

※如果Test.xsd的elementFormDefault是qualified,加不加「aa:」都可以(全域、區域)


※Test.xml

<aa:root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# Test.xsd"
    xmlns:aa="!@#">
    
    <q1>
        <s1></s1>
        <s2>
            <s3></s3>
        </s2>
    </q1>
    <q2>
        <s3></s3>
    </q2>
</aa:root>

※如果Test.xsd的elementFormDefault是unqualified,區域的q1、q2、s1~s3不能加「aa:」

※由於不能加就不曉得是哪個命名空間,所以都建議用qualified,用eclipse預設產生出來的也是qualified



※全域element

再舉個例子,全部都是全域的element

※Test.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="!@#"
    xmlns:a="!@#" elementFormDefault="qualified">
    
    <element name="out" type="a:t1" />
    
    <element name="root">
        <complexType>
            <sequence>
                <element ref="a:out" />
            </sequence>
        </complexType>
    </element>
    
    <complexType name="t1">
        <sequence>
            <element name="s1" type="string" />
            <element name="s2" type="a:t2" />
        </sequence>
    </complexType>
    
    <complexType name="t2">
        <sequence>
            <element name="s3" type="string" />
        </sequence>
    </complexType>
</schema>

※ref連到全域的out,而out是全域的


※Test.xml

<aa:root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# Test.xsd" 
    xmlns:aa="!@#">
    
    <aa:out>
        <aa:s1>a:s1</aa:s1>
        <aa:s2>
            <aa:s3>a:s3</aa:s3>
        </aa:s2>
    </aa:out>
</aa:root>

※如果Test.xsd的elementFormDefault是qualified,加不加「aa:」都可以(全域、區域)


※Test.xml

<aa:root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# Test.xsd" xmlns:aa="!@#">
    
    <aa:out>
        <s1>a:s1</s1>
        <s2>
            <s3>a:s3</s3>
        </s2>
    </aa:out>
</aa:root>

※如果Test.xsd的elementFormDefault是unqualified,區域的s1~s3不能加「aa:」



※attributeFormDefault

和elementFormDefault是針對元性,這裡是針對屬性
屬性一定是元素的其中之一,所以建議是unqualified,用eclipse預設不會產生attributeFormDefault,而attributeFormDefault預設值就是unqualified


※Test.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="!@#"
    xmlns:a="!@#" 
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">
    
    <element name="root1">
        <complexType>
            <sequence>
                <element name="first" type="string" />
                <element name="second">
                    <complexType>
                        <attribute name="r3" type="string" use="required" />
                    </complexType>
                </element>
                <element name="third" type="a:root2" />
            </sequence>
    
            <attribute name="r1" type="string" use="required" />
            <attribute name="r2" type="string" use="required" />
        </complexType>
    </element>
    
    <complexType name="root2">
        <attribute name="r1" type="int" use="required" />
        <attribute name="r2" type="int" use="required" />
    </complexType>
</schema>

※元素root1有屬性r1和r2;子元素second有屬性r3

※子元素third連到外部屬性root2,也有r1、r2兩個屬性


※Test.xml

<a:root1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# Test.xsd"
    xmlns:a="!@#"
    r1="" 
    r2="">
    
    <a:first>a:first</a:first>
    <a:second r3="" />
    <a:third r1="1" r2="2"></a:third>
</a:root1>

※如果Test.xsd的attributeFormDefault是unqualified,所有的區域屬性都不能加「a:」


※Test.xml

<a:root1 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# Test.xsd"
    xmlns:a="!@#"
    a:r1="" 
    a:r2="">
    
    <a:first>a:first</a:first>
    <a:second a:r3="" />
    <a:third a:r1="0" a:r2="0" />
</a:root1>

※如果Test.xsd的attributeFormDefault是qualified,所有的屬性都要加「a:」(全域、區域)



※全域attribute

※Test.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="!@#"
    xmlns:a="!@#" 
    elementFormDefault="qualified"
    attributeFormDefault="unqualified">
    
    <element name="root1">
        <complexType>
            <sequence>
                <element name="first" type="string" />
                <element name="second">
                    <complexType>
                        <attribute ref="a:out" use="required" />
                    </complexType>
                </element>
                <element name="third" type="a:root2" />
            </sequence>
    
            <attribute name="r1" type="string" use="required" />
            <attribute ref="a:out" use="required" />
        </complexType>
    </element>
    
    <complexType name="root2">
        <attribute name="r1" type="int" use="required" />
        <attribute name="r2" type="int" use="required" />
    </complexType>
    
    <attribute name="out" type="string" />
</schema>

※和上面的程式相比較,多了out的全域屬性,然後root1和second都連到外部的out


※Test.xml

<a:root1 a:out="" r1="" xmlns:a="!@#"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# Test.xsd">
    
    <a:first>a:first</a:first>
    <a:second a:out="" />
    <a:third r1="0" r2="0" />
</a:root1>

※如果Test.xsd的attributeFormDefault是qualified,還是和剛剛一構,所有的屬性都要加「a:」(全域、區域)

※如果Test.xsd的attributeFormDefault是unqualified,所有的區域屬性都不能加「a:」,但out是全域屬性,還是要加「a:」

2016年11月4日 星期五

Schema 元素、子元素、命名空間

※為什麼要有schema

最主要是因為它的名稱不能重覆

※Test.dtd

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
    <!ELEMENT root (first, second)>
    <!ELEMENT first (#PCDATA)>
    <!ELEMENT second (aaa?)>
    <!ELEMENT aaa (#PCDATA)>
]>
<root>
    <first>one</first>
    <second></second>
</root>

※root下有first和second,second下有aaa,這沒有問題
但如果second下也要有first那就驗證不過,在開發中最常用到的名稱,可能有name、id、type…等,只能有一個,也未免太過份了,所以才又發明另外一種技術,Schema就出現了


※Test.xsd

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
    <element name="root">
        <complexType>
            <sequence>
                <element name="first" type="string" />
                <element name="second">
                    <complexType>
                        <sequence>
                            <element name="first" type="string"/>
                        </sequence>
                    </complexType>
                </element>
            </sequence>
        </complexType>
    </element>
</schema>

※此時second下可以有first了

※第二行可以進去看看,其實驗證還是DTD


※基本用法

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema">
    <element name="root">
        <complexType>
            <sequence>
                <element name="first" type="string" />
                <element name="second" type="string" />
    
                <element name="third">
                    <complexType>
                        <sequence>
                            <element name="t1" type="string" />
                            <element name="t2" type="string" />
                        </sequence>
                    </complexType>
                </element>
    
                <element name="fourth" type="string" />
            </sequence>
        </complexType>
    </element>
</schema>

※裡面的語法先大概了解就好,下一篇會說明

※上面的程式碼,寫在xsd或xml都是可以的

※xmlns裡面已經定義標籤給我們用了,像element可以用,都是這裡定義好的

※基本的用法就是這樣,element的name是要產生的元素名,type是要產生的元素型態

※third裡面還有子元素



※namespace的前綴

<xxx:schema xmlns:xxx="http://www.w3.org/2001/XMLSchema">
    <xxx:element name="root">
        <xxx:complexType>
            <xxx:sequence>
                <xxx:element name="first" type="xxx:string" />
                <xxx:element name="second" type="xxx:string" />
    
                <xxx:element name="third">
                    <xxx:complexType>
                        <xxx:sequence>
                            <xxx:element name="t1" type="xxx:string" />
                            <xxx:element name="t2" type="xxx:string" />
                        </xxx:sequence>
                    </xxx:complexType>
                </xxx:element>
    
                <xxx:element name="fourth" type="xxx:string" />
            </xxx:sequence>
        </xxx:complexType>
    </xxx:element>
</xxx:schema>

※在xmlns後加上「:xxx」,xxx可以隨便打

※只要一打,全部用到這個namespace的tag也要在前面加上「xxx:」,包括schema、type

※xmlns可以有多個,前綴也可以很多,只要不重覆就行;但沒有前綴只能一個



※引用namespace

※Test.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema" 
    targetNamespace="!@#"
    elementFormDefault="qualified">
    
    <!-- ... -->
</schema>

※…的程式碼和上面一面,只是在schema增加屬性targetNamespace,裡面可以隨便打,表示這一頁所定義的tag存在此命名空間,如first、second…等

※elementFormDefault 下一篇才會說明,這裡先固定寫qualified



※Test.xml

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="!@# ../Test2.xsd" 
    xmlns="!@#">
    
    <first>p:first</first>
    <second>p:second</second>
    <third>
        <t1>p:t1</t1>
        <t2>p:t2</t2>
    </third>
    <fourth>p:fourth</fourth>
</root>

※第一行的XMLSchema-instance不能改,表示要實作第二行 schemaLocation裡的xsd tag

※因為第一行有前綴xsi,所以第二行也要用,因為schemaLocation是這個命名空間裡的東西

※schemaLocation有兩個參數,用空格隔開
.第一個是參數是namespace名稱,從第二個參數的路徑的xsd抓到targetNamespace。
   以這個例子就是Test.xsd裡面targetNamespace的字串,兩個要一樣

.第二個參數是xsd的路徑:也可以用完整路徑,和DTD一樣,用完整路徑時,前面還要有「file:///」

※schemaLocation可以引用多個xsd,一樣用空格隔開,下面還會有範例

※xmlns表示要用哪個命名空間,雖然schemaLocation已經有tag了,但要用時,還要加上這個命名空間,裡面打上namespace的名字
因為schemaLocation可以用多個xsd,它不知道你要用哪一個



※引用多個namespace

※Test.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="123"
    elementFormDefault="qualified">
    
    <element name="root">
        <complexType>
            <sequence>
                <element name="first" type="string" />
                <element name="second" type="string" />
                <element name="third" type="string" />
            </sequence>
        </complexType>
    </element>
</schema>

※

※Test2.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema"
    targetNamespace="456"
    elementFormDefault="qualified">
    
    <element name="root">
        <complexType>
            <sequence>
                <element name="first-1" type="string" />
                <element name="second-1" type="string" />
                <element name="third-1" type="string" />
            </sequence>
        </complexType>
    </element>
</schema>

※tag和Test.xsd故意多個-1

※XXX.xml

<oooo:root xsi:schemaLocation="123 ../Test.xsd 456 ../Test2.xsd"
    xmlns:oooo="123" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
    <oooo:first>first</oooo:first>
    <oooo:second>second</oooo:second>
    <oooo:third>third</oooo:third>
    
    <!-- <oooo:first-1>first-1</oooo:first-1> -->
    <!-- <oooo:second-1>second-1</oooo:second-1> -->
    <!-- <oooo:third-1>thrid-1</oooo:third-1> -->
</oooo:root>

※此時使用123就要選上面那一組,456就選下面那一組



※引用no namespace

※Test.xsd

<schema xmlns="http://www.w3.org/2001/XMLSchema">
    <element name="root">
        <complexType>
            <sequence>
                <element name="first" type="string"></element>
                <element name="second" type="string"></element>
                <element name="third" type="string"></element>
            </sequence>
        </complexType>
    </element>
</schema>

※不要寫targetNamespace



※Test.xml

<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:noNamespaceSchemaLocation="../Test.xsd">
    <first>1</first>
    <second>2</second>
    <third>3</third>
</root>

※使用noNamespaceSchemaLocation即可,裡面不能指定多個,也只能有一個noNamespaceSchemaLocation屬性

※如果Test.xsd裡有targetNamespace屬性,驗證不會過

2016年11月3日 星期四

DTD

參考資料(英文版)
參考資料(簡中版)
XML編程通常都會用XML spy這種工具,但我這裡用的是Eclipse,首先介紹如何新建xml
在package按右建New-->Other...會出現如下的畫面
 紅字是副檔名,這篇會用到dtd和xml,xsd下一篇會講,這裡先選擇xml,然後直接Finish


※Test.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
    <!ELEMENT root (first, second, third, fourth)>
    <!ELEMENT first (#PCDATA)>
    <!ELEMENT second (#PCDATA)>
    
    <!ELEMENT third (t1, t2)>
    <!ELEMENT t1 (#PCDATA)>
    <!ELEMENT t2 (#PCDATA)>
    
    <!ELEMENT fourth (#PCDATA)>
]>
<root>
    <first>one</first>
    <second>
        <![CDATA[
            &
        ]]>
    </second>
    <third>
        <t1>three-one</t1>
        <t2>three-two</t2>
    </third>
    <fourth>four</fourth>
</root>

※root是xml最外層的tag,只要是xml tag,都可以隨便打,圓括號裡的是它的子元素

※#PCDATA表示裡面沒有子元素,所以就這麼一種

※third那行表示其下還有兩個子元素

※<second>裡有CDATA將「&」包起來,是因為在xml裡有五個符號直接打出來會編譯錯誤,因為xml拿來分析用的,例如一讀到「<」就表示標籤的開始,這五個標籤分別是
「<」=「&lt;」
「>」= 「&gt;」
「&」=「&amp;」
「'」=「&apos;」
「"」=「&quot;」
要打這五個符號就用等號右邊的方式打出來

但假設我想打「if(a>b & a < c)」,這在寫程式裡常常用到,如果我一篇文章打下來都用等號右邊的那種方式,可讀性不高,所以又有了CDATA的方式來使用,只要XML讀到
「<![CDATA[」就知道之後的字都不管,一直讀到有「]]>」為止

※元素名稱後面一定要有空格

※可以在編輯程式碼的地方按右鍵Validate

出現左上角的圖示才表示程式是沒有問題的,不會驗證第一行的UTF-8,把它刪除也可驗證成功



※四種符號和ANY、EMPTY

<!DOCTYPE root [
    <!ELEMENT root (first?, second+, third*, (fourth1|fourth2|fourth3), (f1|f2|f3)?, fifth, sixth)>
    <!ELEMENT first (#PCDATA)>
    <!ELEMENT second (#PCDATA)>
    
    <!ELEMENT third (t1?, t2)>
    <!ELEMENT t1 (#PCDATA)>
    <!ELEMENT t2 (#PCDATA)>
    
    <!ELEMENT fourth (#PCDATA)>
    <!ELEMENT fifth EMPTY>
    <!ELEMENT sixth ANY>
]>
<root>
    <second>second</second>
    <third>
        <t1>three-one</t1>
        <t2>three-two</t2>
    </third>
    <third>
        <t2>three-two</t2>
    </third>
    <fourth1>four1</fourth1>
    <fifth />
    <sixth>
        <third>
            <t1>three-one</t1>
            <t2>three-two</t2>
        </third>
    </sixth>
</root>

※
?:0~1
+:1~多
*:0~多
|:多擇一
上個程式碼的子元素名稱,後面都沒有加如上的四個符號,就表示一定要出現,而且只能出現1次

※ANY:還可以包括其他的xml tag,沒定義的當然不行

※EMPTY:內容只能是空的,有兩種方式
「<fitth />」和「<fifth></fifth>」



※引用dtd

就是dtd和xml分開,可以讓很多xml用
先新增一個dtd檔,就是第一張圖選DTD File

※test.dtd

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT root (first?, second+, third*, (fourth1|fourth2|fourth3), (f1|f2|f3)?, fifth, sixth)>
<!ELEMENT first (#PCDATA)>
<!ELEMENT second (#PCDATA)>
    
<!ELEMENT third (t1?, t2)>
<!ELEMENT t1 (#PCDATA)>
<!ELEMENT t2 (#PCDATA)>
    
<!ELEMENT fourth1 (#PCDATA)>
<!ELEMENT fourth2 (#PCDATA)>
<!ELEMENT fourth3 (#PCDATA)>
    
<!ELEMENT f1 (#PCDATA)>
<!ELEMENT f2 (#PCDATA)>
<!ELEMENT f3 (#PCDATA)>
    
<!ELEMENT fifth EMPTY>
<!ELEMENT sixth ANY>

※此時裡面只能有dtd的語法,不然驗證會錯,也就是不要DOCTYPE開頭

※新增xml引用dtd
如果新增xml直接finish,預設是最下面的XML template,schema下一篇會說明



選剛剛新增的dtd,另外一個radio button是系統的dtd,想看裡面的內容可以到
Window-->Preferences-->XML-->XML catalog
就可以在本機找到jar,然後再用解壓縮軟體繼續開啟裡面的路徑

※繼續下一步後
如果dtd驗證沒過,Root element的下拉就沒得選,且Finish也是disabled


※Test.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "../test.dtd" >
<root>
    <second>second</second>
    <third>
        <t1>three-one</t1>
        <t2>three-two</t2>
    </third>
    <third>
        <t2>three-two</t2>
    </third>
    <fourth1>four1</fourth1>
    <fifth />
    <sixth />
</root>

※DOCTYPE的root是頂層tag的名稱

※路徑還可以用完整路徑,但前面要加file:///,如「file:///D:\abc\test.dtd」

※雖然沒加file:///,按ctrl再按路徑名稱也可以連的到,但驗證就是不會過

※SYSTEM那裡有兩個選項,另一個是PUBLIC
SYSTEM:表示私人的意思
PUBLIC:表示公用的意思

※用Eclipse新增jsp檔時會出現如下的DOCTYPE
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">

※html當然就是最頂層的tag了

※有兩個字串,第一個字串的第一個字有分三種
「ISO」:表示ISO組織批准的
「+」:表示標準組織制定的,但不是ISO組織批准的
「-」:表示非標準組織制定的

後面再接DTD的擁有者
再來是DTD的描述
再來是語言

※第二個字串是DTD的路徑



※屬性

※Test.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
    <!ELEMENT root (first, second)>
    <!ELEMENT first (#PCDATA)>
    <!ATTLIST first ooo CDATA #IMPLIED>
    <!ATTLIST first id ID #REQUIRED>
    <!ATTLIST first zzz CDATA #REQUIRED>
    
    <!ELEMENT second (#PCDATA)>
    <!ATTLIST second xxx CDATA 'defalut'>
    <!ATTLIST second type CDATA #FIXED "fix value">
]>
<root>
    <first id="a" zzz="">one</first>
    <second xxx='sss'>sss</second>
</root>

※ATTLIST表示要宣告屬性,然後元素名、屬性名、型態、預設值,用空格隔開

※這裡的CDATA和上面介紹的CDATA意思不一樣,這裡是指字串的型態,還有很多,請參考w3c網站

※預設值除了寫死的以外,還有三種方式
#REQUIRED:必要的
#IMPLIED:可選的
#FIXED value:固定值的,所以後面有值;使用者可以不寫,但一寫就有寫這裡設定的值,否則驗證過不了

※ID型態表示唯一,且一定要有值(空格、空白都不行),如上面的id和zzz,zzz可以是空的



※實體

很像變數的功能,有兩種,「&」和「%」
&:在xml、dtd定義都可以,使用時只能在xml使用,使用時,用「&」和「;」包起來即可
%:定義和使用都只能在dtd,使用時,用「%」和「;」包起來即可

※Test.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root [
    <!ELEMENT root (first, second)>
    <!ELEMENT first (#PCDATA)>
    <!ELEMENT second (#PCDATA)>
    
    <!ENTITY xxx "abc">
    <!ENTITY ooo "def">
]>
<root>
    <first>&xxx;</first>
    <second>&ooo;</second>
</root>

※xxx是abc;ooo是def,要使用變數時,要用「&」和「;」包起來即可

※因為%只能在dtd,所以以下用dtd檔

※test.dtd

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT root (first, second)>
<!ELEMENT first (#PCDATA)>
<!ELEMENT second (#PCDATA)>
    
<!ENTITY xxx "abc">
<!ENTITY % zzz "def">
<!ATTLIST second %zzz; CDATA 'a'>

※定義zzz的dtd變數為def,而元素second的屬性就是def

※test.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE root SYSTEM "Test.dtd" >
<root>
    <first>&xxx;</first>
    <second def="a">%zzz;</second>
</root>

※此時%zzz;當然不會出現def,只是寫死的字串而已

※我用XML Spy的browser的功能,試&功能沒成功,也沒有錯誤,只是出來的結果是空的,不知為什麼?

※如果dtd/xsd改了,xml一時之間不知道要打什麼,可以將xml刪除,重新增加一個xml檔,import進來就會產生一些值了,比較不會打錯,也可以如下的方式一點一點增加
在Eclipse的Window-->Perspective-->Open Perspective-->Other...
然後選XML視圖,會有Outline可用,按右鍵可發現很多功能