如何读写XML文件?

87

我需要读取和写入XML文件,使用Java读写XML文件最简单的方法是什么?


通过StAX实现内存高效的部分XML解析:https://stackoverflow.com/questions/52614082/parsing-highly-nested-xml-without-dom-in-java - Vadzim
7个回答

145

这是一个快速的DOM示例,展示了如何读写一个带有DTD的简单XML文件:

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE roles SYSTEM "roles.dtd">
<roles>
    <role1>User</role1>
    <role2>Author</role2>
    <role3>Admin</role3>
    <role4/>
</roles>

还有DTD:

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT roles (role1,role2,role3,role4)>
<!ELEMENT role1 (#PCDATA)>
<!ELEMENT role2 (#PCDATA)>
<!ELEMENT role3 (#PCDATA)>
<!ELEMENT role4 (#PCDATA)>

首先导入这些:

import javax.xml.parsers.*;
import javax.xml.transform.*;
import javax.xml.transform.dom.*;
import javax.xml.transform.stream.*;
import org.xml.sax.*;
import org.w3c.dom.*;

以下是您需要的几个变量:

private String role1 = null;
private String role2 = null;
private String role3 = null;
private String role4 = null;
private ArrayList<String> rolev;

这是一个读取器(String xml是您的xml文件名):

public boolean readXML(String xml) {
        rolev = new ArrayList<String>();
        Document dom;
        // Make an  instance of the DocumentBuilderFactory
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        try {
            // use the factory to take an instance of the document builder
            DocumentBuilder db = dbf.newDocumentBuilder();
            // parse using the builder to get the DOM mapping of the    
            // XML file
            dom = db.parse(xml);

            Element doc = dom.getDocumentElement();

            role1 = getTextValue(role1, doc, "role1");
            if (role1 != null) {
                if (!role1.isEmpty())
                    rolev.add(role1);
            }
            role2 = getTextValue(role2, doc, "role2");
            if (role2 != null) {
                if (!role2.isEmpty())
                    rolev.add(role2);
            }
            role3 = getTextValue(role3, doc, "role3");
            if (role3 != null) {
                if (!role3.isEmpty())
                    rolev.add(role3);
            }
            role4 = getTextValue(role4, doc, "role4");
            if ( role4 != null) {
                if (!role4.isEmpty())
                    rolev.add(role4);
            }
            return true;

        } catch (ParserConfigurationException pce) {
            System.out.println(pce.getMessage());
        } catch (SAXException se) {
            System.out.println(se.getMessage());
        } catch (IOException ioe) {
            System.err.println(ioe.getMessage());
        }

        return false;
    }

这里是一个作家:

public void saveToXML(String xml) {
    Document dom;
    Element e = null;

    // instance of a DocumentBuilderFactory
    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    try {
        // use factory to get an instance of document builder
        DocumentBuilder db = dbf.newDocumentBuilder();
        // create instance of DOM
        dom = db.newDocument();

        // create the root element
        Element rootEle = dom.createElement("roles");

        // create data elements and place them under root
        e = dom.createElement("role1");
        e.appendChild(dom.createTextNode(role1));
        rootEle.appendChild(e);

        e = dom.createElement("role2");
        e.appendChild(dom.createTextNode(role2));
        rootEle.appendChild(e);

        e = dom.createElement("role3");
        e.appendChild(dom.createTextNode(role3));
        rootEle.appendChild(e);

        e = dom.createElement("role4");
        e.appendChild(dom.createTextNode(role4));
        rootEle.appendChild(e);

        dom.appendChild(rootEle);

        try {
            Transformer tr = TransformerFactory.newInstance().newTransformer();
            tr.setOutputProperty(OutputKeys.INDENT, "yes");
            tr.setOutputProperty(OutputKeys.METHOD, "xml");
            tr.setOutputProperty(OutputKeys.ENCODING, "UTF-8");
            tr.setOutputProperty(OutputKeys.DOCTYPE_SYSTEM, "roles.dtd");
            tr.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");

            // send DOM to file
            tr.transform(new DOMSource(dom), 
                                 new StreamResult(new FileOutputStream(xml)));

        } catch (TransformerException te) {
            System.out.println(te.getMessage());
        } catch (IOException ioe) {
            System.out.println(ioe.getMessage());
        }
    } catch (ParserConfigurationException pce) {
        System.out.println("UsersXML: Error trying to instantiate DocumentBuilder " + pce);
    }
}
这里是getTextValue的取值:
private String getTextValue(String def, Element doc, String tag) {
    String value = def;
    NodeList nl;
    nl = doc.getElementsByTagName(tag);
    if (nl.getLength() > 0 && nl.item(0).hasChildNodes()) {
        value = nl.item(0).getFirstChild().getNodeValue();
    }
    return value;
}

添加一些访问器和修改器,你就完成了!


7
这个工作需要DTD文件吗?或者我们可以在没有DTD文件的情况下读取XML吗?如果需要DTD文件,我们能否轻松地从XML生成一个DTD,而不是自己输入所有内容? - user2603796
3
你可以不使用dtd文件。确保你还要从xml文件中删除对它的引用:<!DOCTYPE roles SYSTEM "roles.dtd">。你可以找到免费的dtd生成器应用程序或使用在线服务。它们会生成一个足够好的dtd文件以供开始使用。通常你需要稍微修改一下它。 - Costis Aivalis
很好的例子!我在getTextValue()函数上遇到了一个小问题,如果节点为空,它应该返回一个零长度的字符串而不是null。我不得不在“return value”之前添加“if(value==null) value = "";",否则在写入XML字符串时会崩溃。 - Mr Ed
@CostisAivalis,你能告诉我如何在Batik中解析和保存SVG吗? 我在区分XML和Batik的工作方式方面遇到了问题。这里 - Mirwise Khan

17

使用JAXB(Java Architecture for XML Binding)编写XML:

http://www.mkyong.com/java/jaxb-hello-world-example/

package com.mkyong.core;

import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
public class Customer {

    String name;
    int age;
    int id;

    public String getName() {
        return name;
    }

    @XmlElement
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    @XmlElement
    public void setAge(int age) {
        this.age = age;
    }

    public int getId() {
        return id;
    }

    @XmlAttribute
    public void setId(int id) {
        this.id = id;
    }

} 

package com.mkyong.core;

import java.io.File;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;

public class JAXBExample {
    public static void main(String[] args) {

      Customer customer = new Customer();
      customer.setId(100);
      customer.setName("mkyong");
      customer.setAge(29);

      try {

        File file = new File("C:\\file.xml");
        JAXBContext jaxbContext = JAXBContext.newInstance(Customer.class);
        Marshaller jaxbMarshaller = jaxbContext.createMarshaller();

        // output pretty printed
        jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        jaxbMarshaller.marshal(customer, file);
        jaxbMarshaller.marshal(customer, System.out);

          } catch (JAXBException e) {
        e.printStackTrace();
          }

    }
}

11
以上的回答只涉及DOM解析器(通常将整个文件读入内存并解析,对于大文件是一个问题),你可以使用SAX解析器,它使用更少的内存,并且速度更快(无论如何这取决于您的代码)。
当SAX解析器发现元素的开始、结束、属性、元素之间的文本等时,会回调一些函数,因此它可以解析文档,同时您也可以得到所需内容。
以下是一些示例代码: http://www.mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/

4
答案仅涵盖DOM / SAX和JAXB示例的复制粘贴实现。
然而,使用XML时缺少一个重要领域。在许多项目/程序中,需要存储/检索一些基本数据结构。您的程序已经有了用于漂亮而闪亮的业务对象/数据结构的类,您只需要一种舒适的方式将这些数据转换为XML结构,以便可以对其进行更多魔法(存储,加载,发送,使用XSLT操纵)。
这就是XStream的优点所在。您只需注释包含数据的类,或者如果您不想更改那些类,则为编组(对象-> xml)或取消编组(xml->对象)配置XStream实例。
在内部,XStream使用反射,标准Java对象序列化的readObject和readResolve方法。
您可以在此处获得良好且迅速的教程:
为了简要概述它的工作原理,我还提供了一些样本代码,其中演示了如何编组和取消编组数据结构。 编组/取消编组全部在main方法中完成,其余代码只是生成一些测试对象并向其填充一些数据。 配置xStream实例非常简单,并且编组/取消编组每个都只需要一行代码。
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import com.thoughtworks.xstream.XStream;

public class XStreamIsGreat {

  public static void main(String[] args) {
    XStream xStream = new XStream();
    xStream.alias("good", Good.class);
    xStream.alias("pRoDuCeR", Producer.class);
    xStream.alias("customer", Customer.class);

    Producer a = new Producer("Apple");
    Producer s = new Producer("Samsung");
    Customer c = new Customer("Someone").add(new Good("S4", 10, new BigDecimal(600), s))
        .add(new Good("S4 mini", 5, new BigDecimal(450), s)).add(new Good("I5S", 3, new BigDecimal(875), a));
    String xml = xStream.toXML(c); // objects -> xml
    System.out.println("Marshalled:\n" + xml);
    Customer unmarshalledCustomer = (Customer)xStream.fromXML(xml); // xml -> objects
  }

  static class Good {
    Producer producer;

    String name;

    int quantity;

    BigDecimal price;

    Good(String name, int quantity, BigDecimal price, Producer p) {
      this.producer = p;
      this.name = name;
      this.quantity = quantity;
      this.price = price;
    }

  }

  static class Producer {
    String name;

    public Producer(String name) {
      this.name = name;
    }
  }

  static class Customer {
    String name;

    public Customer(String name) {
      this.name = name;
    }

    List<Good> stock = new ArrayList<Good>();

    Customer add(Good g) {
      stock.add(g);
      return this;
    }
  }
}

1

好的,已经在答案列表中提到了DOM、JaxB和XStream,但是读写XML还有一种完全不同的方式:数据投影。您可以使用一个库来提供可读和可写的视图,将XML结构和Java结构解耦为Java接口。来自教程

给出一些真实世界的XML:

<weatherdata>
  <weather
    ... 
    degreetype="F"
    lat="50.5520210266113" lon="6.24060010910034" 
    searchlocation="Monschau, Stadt Aachen, NW, Germany" 
            ... >
    <current ... skytext="Clear" temperature="46"/>
  </weather>
</weatherdata>

使用数据投影,您可以定义一个投影接口:
public interface WeatherData {

@XBRead("/weatherdata/weather/@searchlocation")   
String getLocation();

@XBRead("/weatherdata/weather/current/@temperature")
int getTemperature();

@XBRead("/weatherdata/weather/@degreetype")
String getDegreeType();

@XBRead("/weatherdata/weather/current/@skytext")
String getSkytext();

/**
 * This would be our "sub projection". A structure grouping two attribute
 * values in one object.
 */
interface Coordinates {
    @XBRead("@lon")
    double getLongitude();

    @XBRead("@lat")
    double getLatitude();
}

@XBRead("/weatherdata/weather")
Coordinates getCoordinates();
}

使用此接口的实例就像普通Java对象一样:

private void printWeatherData(String location) throws IOException {

final String BaseURL = "http://weather.service.msn.com/find.aspx?outputview=search&weasearchstr=";

// We let the projector fetch the data for us
WeatherData weatherData = new XBProjector().io().url(BaseURL + location).read(WeatherData.class);

// Print some values
System.out.println("The weather in " + weatherData.getLocation() + ":");
System.out.println(weatherData.getSkytext());
System.out.println("Temperature: " + weatherData.getTemperature() + "°"
                                   + weatherData.getDegreeType());

// Access our sub projection
Coordinates coordinates = weatherData.getCoordinates();
System.out.println("The place is located at " + coordinates.getLatitude() + ","
                                              + coordinates.getLongitude());
}

即使用于创建XML,XPath表达式也可以被写入。

0

SAX解析器与DOM解析器的工作方式不同,它既不将任何XML文档加载到内存中,也不创建任何XML文档的对象表示。相反,SAX解析器使用回调函数org.xml.sax.helpers.DefaultHandler来通知客户端XML文档结构。

SAX解析器比DOM解析器更快,使用的内存更少。 以下是SAX回调方法:

startDocument()endDocument() - 在XML文档的开头和结尾调用的方法。 startElement()endElement() - 在文档元素的开头和结尾调用的方法。 characters() - 在XML文档元素的开始和结束标记之间调用的包含文本内容的方法。

  1. XML文件

创建一个简单的XML文件。

<?xml version="1.0"?>
<company>
    <staff>
        <firstname>yong</firstname>
        <lastname>mook kim</lastname>
        <nickname>mkyong</nickname>
        <salary>100000</salary>
    </staff>
    <staff>
        <firstname>low</firstname>
        <lastname>yin fong</lastname>
        <nickname>fong fong</nickname>
        <salary>200000</salary>
    </staff>
</company>

XML解析器: Java文件使用SAX解析器来解析XML文件。
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class ReadXMLFile {
    public static void main(String argv[]) {

        try {
            SAXParserFactory factory = SAXParserFactory.newInstance();
            SAXParser saxParser = factory.newSAXParser();

            DefaultHandler handler = new DefaultHandler() {
                boolean bfname = false;
                boolean blname = false;
                boolean bnname = false;
                boolean bsalary = false;

                public void startElement(String uri, String localName,String qName, 
                            Attributes attributes) throws SAXException {

                    System.out.println("Start Element :" + qName);

                    if (qName.equalsIgnoreCase("FIRSTNAME")) {
                        bfname = true;
                    }

                    if (qName.equalsIgnoreCase("LASTNAME")) {
                        blname = true;
                    }

                    if (qName.equalsIgnoreCase("NICKNAME")) {
                        bnname = true;
                    }

                    if (qName.equalsIgnoreCase("SALARY")) {
                        bsalary = true;
                    }

                }

                public void endElement(String uri, String localName,
                    String qName) throws SAXException {

                    System.out.println("End Element :" + qName);

                }

                public void characters(char ch[], int start, int length) throws SAXException {

                    if (bfname) {
                        System.out.println("First Name : " + new String(ch, start, length));
                        bfname = false;
                    }

                    if (blname) {
                        System.out.println("Last Name : " + new String(ch, start, length));
                        blname = false;
                    }

                    if (bnname) {
                        System.out.println("Nick Name : " + new String(ch, start, length));
                        bnname = false;
                    }

                    if (bsalary) {
                        System.out.println("Salary : " + new String(ch, start, length));
                        bsalary = false;
                    }

                }

            };

            saxParser.parse("c:\\file.xml", handler);

        } catch (Exception e) {
           e.printStackTrace();
        }

    }

}

结果

开始元素: 公司
开始元素: 员工
开始元素: 名字
名字:勇
结束元素: 名字
开始元素: 姓
姓: Mook Kim
结束元素: 姓
开始元素: 昵称
昵称:Mkyong
结束元素: 昵称
等等...

来源(MyKong) - http://www.mkyong.com/java/how-to-read-xml-file-in-java-sax-parser/


0
这是一个保存XML文件的示例代码。
public void save(String tableName, List<Customer> customers) {

    File f = new File(FileUtil.getTableFileName(tableName));

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    try {
        // make the xml tree
        // use factory to get the instance of document builder
        DocumentBuilder db = dbf.newDocumentBuilder();
        Document doc = db.newDocument();
        // Root element <customers>
        Element rootElem = doc.createElement(Customer.KEY_ROOT);
        doc.appendChild(rootElem);

        for (Customer customer : customers) {
            Element customerElem = doc.createElement(Customer.KEY_ELEMENT);
            rootElem.appendChild(customerElem);

            // child element <ID> under <customer>
            Element cid = doc.createElement(Customer.KEY_ID);
            cid.appendChild(doc.createTextNode(Integer.toString(customer.getId())));
            customerElem.appendChild(cid);

            if (customer.getName() != null) {
                Element name = doc.createElement(Customer.KEY_NAME);
                name.appendChild(doc.createTextNode(customer.getName()));
                customerElem.appendChild(name);
            }

            if (customer.getAddress() != null) {
                Element address = doc.createElement(Customer.KEY_ADDRESS);
                address.appendChild(doc.createTextNode(customer.getAddress()));
                customerElem.appendChild(address);
            }

            if (customer.getCity() != null) {
                Element city = doc.createElement(Customer.KEY_CITY);
                city.appendChild(doc.createTextNode(customer.getCity()));
                customerElem.appendChild(city);
            }

            if (customer.getPostCode() != null) {
                Element postCode = doc.createElement(Customer.KEY_POSTCODE);
                postCode.appendChild(doc.createTextNode(customer.getPostCode()));
                customerElem.appendChild(postCode);
            }

            if (customer.getCountry() != null) {
                Element country = doc.createElement(Customer.KEY_COUNTRY);
                country.appendChild(doc.createTextNode(customer.getCountry()));
                customerElem.appendChild(country);
            }
        }
        // save the xml file
        // Transformer is for process XML from a variety of sources and write the
        // transformation
        // output to a variety of sinks
        Transformer transformer = TransformerFactory.newInstance().newTransformer();

        // set xml encoding to utf-8
        transformer.setOutputProperty(OutputKeys.ENCODING, "utf-8");
        // pretty print
        transformer.setOutputProperty(OutputKeys.INDENT, "yes");

        DOMSource source = new DOMSource(doc);
        StreamResult result = new StreamResult(f);
        transformer.transform(source, result);

    } catch (Exception e) {
        e.printStackTrace();
    }
}

这是一个阅读的例子:
    public static List<List<String>> read(String fileName) {
    List<List<String>> result = new ArrayList<>();
    File f = new File(fileName);
    if (!f.exists()) {
        return result;
    }

    DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
    DocumentBuilder db;
    try {
        db = dbf.newDocumentBuilder();
        Document doc = db.parse(f);

        doc.getDocumentElement().normalize();

        NodeList StatesNodes = doc.getElementsByTagName(STATE_ROOT_ELEMENT);
        List<String> keys = new ArrayList<>();
        List<String> states = new ArrayList<>();

        for (int i = 0; i < StatesNodes.getLength(); i++) {
            NodeList children = StatesNodes.item(i).getChildNodes();
            for (int j = 0; j < children.getLength(); j++) {
                Node child = children.item(j);
                String childName = child.getNodeName();
                if (child.getNodeType() == Node.ELEMENT_NODE) {
                    keys.add(childName);
                    states.add(child.getTextContent());
                }
            }
        }
        result.add(keys);
        result.add(states);
        
    } catch (Exception e) {
        e.printStackTrace();
    }

    return result;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接