首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

XmlBeans 實戰進階:XML 文件 CRUD 操作與刪除異常深度排查

     編輯:Java 綜合教學

XmlBeans 實戰進階:XML 文件 CRUD 操作與刪除異常深度排查

XmlBeans 作為 Apache 提供的 XML-Java 綁定工具,通過 Schema 編譯生成強類型 Java 類,實現 XML 文檔的對象化操作。本文基於完整案例,演示 XmlBeans 核心 API 使用,並深度剖析 setNil() 刪除操作拋出 XmlValueNotNillableException 的根因與解決方案。

環境準備與 Schema 定義

  1. 定義 customers.xsd 描述客戶數據結構(含嵌套地址、主地址、賬單地址)。
  2. 使用 scomp 編譯生成類:scomp -out xmlbeans.jar customers.xsd。
  3. 將生成的 jar 加入項目 Classpath。

核心操作代碼實戰

public class CustomerXmlBeans {
    private final File xmlFile;

    public CustomerXmlBeans(String filePath) { this.xmlFile = new File(filePath); }

    // 讀取並遍歷
    public void readCustomers() throws Exception {
        CustomersDocument doc = CustomersDocument.Factory.parse(xmlFile);
        for (CustomerType customer : doc.getCustomers().getCustomerArray()) {
            System.out.printf("ID:%s Name:%s %s Phone:%s%n",
                customer.getId(), customer.getFirstname(), customer.getLastname(), customer.getPhoneNumber());
            AddressType addr = customer.getAddress();
            printAddress("Primary", addr.getPrimaryAddress());
            printAddress("Billing", addr.getBillingAddress());
        }
    }

    // 新建文檔並寫入
    public void createCustomer() throws Exception {
        CustomersDocument doc = CustomersDocument.Factory.newInstance();
        CustomerType customer = doc.addNewCustomers().addNewCustomer();
        customer.setId(3);
        customer.setFirstname("Jessica");
        customer.setLastname("Lim");
        customer.setGender("female");
        customer.setPhoneNumber("1234567");

        AddressType addr = customer.addNewAddress();
        PrimaryAddressType pAddr = addr.addNewPrimaryAddress();
        pAddr.setPostalCode("350106");
        pAddr.setAddressLine1("#25-1");
        pAddr.setAddressLine2("SHINSAYAMA 2-CHOME");

        BillingAddressType bAddr = addr.addNewBillingAddress();
        bAddr.setReceiver("Ms Danielle");
        bAddr.setPostalCode("350107");
        bAddr.setAddressLine1("#167");
        bAddr.setAddressLine2("NORTH TOWER HARBOUR CITY");

        doc.save(xmlFile);
    }

    // 追加客戶
    public void addCustomer() throws Exception {
        CustomersDocument doc = CustomersDocument.Factory.parse(xmlFile);
        CustomerType customer = doc.getCustomers().addNewCustomer();
        // ... 設置屬性同 createCustomer ...
        doc.save(xmlFile);
    }

    // 刪除客戶(問題方法)
    public void deleteCustomerById(long id) throws Exception {
        CustomersDocument doc = CustomersDocument.Factory.parse(xmlFile);
        CustomerType[] customers = doc.getCustomers().getCustomerArray();
        for (int i = 0; i < customers.length; i++) {
            if (customers[i].getId() == id) {
                customers[i].setNil(); // ❌ 拋出 XmlValueNotNillableException
                break;
            }
        }
        doc.save(xmlFile);
    }

    private void printAddress(String label, XmlObject addr) { /* ... */ }
}

異常深度剖析:XmlValueNotNillableException

Exception in thread "main" org.apache.xmlbeans.impl.values.XmlValueNotNillableException
    at org.apache.xmlbeans.impl.values.XmlObjectBase.setNil(XmlObjectBase.java:624)
    at com.CustomerXMLBean.deleteCustomer(CustomerXMLBean.java:167)

根因:Schema 定義缺失 nillable="true"

setNil() 語義是「將元素標記為 xsi:nil=true」,**前提是 Schema 中該元素聲明 nillable="true"**。

檢查生成的 CustomerType 接口/類,其對應的 Schema 片段應為:

<xs:element name="customer" type="CustomerType" maxOccurs="unbounded" nillable="true"/>

若原始 XSD 中 <xs:element name="customer" .../> **未聲明 nillable**,編譯生成的類不支持 setNil(),運行時拋出異常。

三種解決方案

方案操作優缺點
方案一:修改 XSD 重新編譯(推薦)在 customers.xsd 中為 customer 元素添加 nillable="true",重新運行 scomp 覆蓋生成的 jar。徹底解決,語義正確。需重新編譯部署。
方案二:物理移除節點(無需 XSD 變更)使用 DOM API 或 XmlBeans 低層 API 移除節點:
customers[i].getDomNode().getParentNode().removeChild(customers[i].getDomNode());
快速修復,不改 Schema。繞過強類型約束,需小心命名空間。
方案三:邏輯刪除標記在 CustomerType 中添加 deleted 布爾字段(需修改 XSD),設置 customer.setDeleted(true),查詢時過濾。業務友好,支持恢復/審計。需 Schema 變更。

方案二代碼示例:物理移除節點

public void deleteCustomerPhysical(long id) throws Exception {
    CustomersDocument doc = CustomersDocument.Factory.parse(xmlFile);
    CustomersDocument.Customers customersElem = doc.getCustomers();
    CustomerType[] customers = customersElem.getCustomerArray();
    for (int i = 0; i < customers.length; i++) {
        if (customers[i].getId() == id) {
            // 獲取底層 DOM 節點並移除
            org.w3c.dom.Node domNode = customers[i].getDomNode();
            domNode.getParentNode().removeChild(domNode);
            break;
        }
    }
    doc.save(xmlFile);
}

最佳實踐總結

  • Schema 先行:設計 XSD 時預見業務操作(增刪改查),必要元素標記 nillable="true"、minOccurs="0"。
  • 版本管理生成代碼:將 scomp 生成的 jar 納入版本控制或私有 Maven 倉庫,避免「本地能跑、CI 掛掉」。
  • 異常即文檔:XmlValueNotNillableException 直接指向 Schema 缺陷,善用異常堆疊定位問題。
  • 考慮替代方案:現代項目推薦 JAXB (Java 內建)、Jackson XML、Simple XML,生態更活躍、API 更友好、性能更優。

結語

XmlBeans 以強類型、XPath/XQuery 支持見長,但在「刪除操作需 Schema 配合」這一設計決策上容易踩坑。理解 nillable 與 setNil() 的綁定關係,掌握 DOM 級降級操作,能讓你在維護舊系統或特定場景下游刃有餘。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved