Apache XMLSchema 在 Ubuntu 22.04 集成使用指南

在 Ubuntu 22.04 服务器上从零引入 Apache XMLSchema(org.apache.ws.xmlschema:xmlschema-core 2.3.3)的完整集成指南,覆盖依赖引入、XSD 加载、对象树遍历、JAXP 校验联动与编程建模,全部示例实测通过。

Apache XMLSchemaApache XMLSchemaJavaXMLUbuntuUbuntu 22.042026/10/16

Apache XMLSchema 在 Ubuntu 22.04 集成使用指南

目标:在 Ubuntu 22.04 服务器上从零完成 Apache XMLSchema(xmlschema-core)的依赖引入,跑通「加载 XSD → 对象树遍历 → JAXP 校验联动 → 编程建模」四组可独立执行的示例。全部命令与回显在 Ubuntu 22.04.5 LTS + OpenJDK 17 + Maven 3.6.3 环境实测通过。

一、环境信息

本章列出实测环境的完整信息,照做即可复现。操作系统与工具链:

grep PRETTY_NAME /etc/os-release

实测输出:

PRETTY_NAME="Ubuntu 22.04.5 LTS"
java -version 2>&1

实测输出:

openjdk version "17.0.20.1" 2026-08-18
OpenJDK Runtime Environment (build 17.0.20.1+1-1-22.04-Ubuntu)
OpenJDK 64-Bit Server VM (build 17.0.20.1+1-1-22.04-Ubuntu, mixed mode, sharing)
mvn -version | head -3

实测输出:

Apache Maven 3.6.3
Maven home: /usr/share/maven
Java version: 17.0.20.1, vendor: Ubuntu, runtime: /usr/lib/jvm/java-17-openjdk-amd64

本文使用的依赖与示例文件统一放在 /root/demo 下,XMLSchema 构件版本为 Maven Central 当前最新稳定版 2.3.3(版本选型依据见《Apache XMLSchema 介绍与选型指南》)。

二、前置条件

本库是纯 Java 库(无独立进程、无 CLI),前置只有 JDK 与 Maven 两样。在全新服务器上安装:

apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq openjdk-17-jdk maven

实测输出(节选,安装包较多时全量输出很长):

Processing triggers for libgdk-pixbuf-2.0-0:amd64 (2.42.8+dfsg-1ubuntu0.5) ...

安装后验证(务必用回显确认版本,不要凭印象):

java -version 2>&1 | head -1 && mvn -version | head -1

实测输出:

openjdk version "17.0.20.1" 2026-08-18
Apache Maven 3.6.3

⚠️ 注意:服务器会话必须是 UTF-8 locale。实测中默认 locale 下程序输出的中文全部变成问号(见第六章排障),执行示例前先固定编码:

export LANG=C.UTF-8 LC_ALL=C.UTF-8

该命令无回显,属正常。创建工程目录结构:

mkdir -p /root/demo/src/main/java/com/example /root/demo/schemas /root/demo/xml /root/demo/out && cd /root/demo

该命令无回显。创建完工程骨架后,最终文件清单如下(find 验证):

find . -type f | sort

实测输出:

./pom.xml
./schemas/company.xsd
./src/main/java/com/example/BuildSchema.java
./src/main/java/com/example/JaxpValidate.java
./src/main/java/com/example/LoadAndTraverse.java
./xml/employee-invalid.xml
./xml/employee-valid.xml

三、依赖引入

在 /root/demo/pom.xml 中写入如下内容(编辑器创建该文件):

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.example</groupId>
  <artifactId>xmlschema-demo</artifactId>
  <version>1.0.0</version>
  <packaging>jar</packaging>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>17</maven.compiler.source>
    <maven.compiler.target>17</maven.compiler.target>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.apache.ws.xmlschema</groupId>
      <artifactId>xmlschema-core</artifactId>
      <version>2.3.3</version>
    </dependency>
  </dependencies>
</project>

坐标逐项说明:groupId 为 org.apache.ws.xmlschema、artifactId 为 xmlschema-core,这是 2.x 时代唯一的正式构件(1.x 时代的 xmlschema 构件已淘汰);版本 2.3.3 是 Maven Central maven-metadata.xml 的 <release> 实测值。该构件零传递依赖(《介绍与选型指南》有依赖树实测),不会拖入其他 jar。

编译验证依赖可用:

mvn -q compile; echo COMPILE_EXIT=$?

实测输出:

COMPILE_EXIT=0
mvn dependency:tree 2>&1 | sed -n '/maven-dependency-plugin/,/BUILD/p' | head -6

实测输出:

[INFO] --- maven-dependency-plugin:2.8:tree (default-cli) @ xmlschema-demo ---
[INFO] com.example:xmlschema-demo:jar:1.0.0
[INFO] \- org.apache.ws.xmlschema:xmlschema-core:jar:2.3.3:compile
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS

💡 说明:exec-maven-plugin 用于直接运行示例类的 main 方法(后文 mvn exec:java 命令),首次运行会自动下载该插件,无需在 POM 里声明版本。

四、核心 API/配置

本章先给出后续示例要用的四个 API 入口,理解了再进第五章动手。

4.1 XmlSchemaCollection:集合入口

XmlSchemaCollection 是一切操作的起点,负责读 XSD、解析 import/include、跨 schema 查找。它的读取入口是一组重载的 read(...):接受 javax.xml.transform.Source、SAX InputSource、Reader、DOM Document/Element 等多种输入(重载清单可用 javap -cp <jar路径> org.apache.ws.commons.schema.XmlSchemaCollection 自查,其中 read(Source) 与 read(InputSource) 是文件场景最常用的两个)。查找类方法三个:getElementByQName(QName)/getTypeByQName(QName) 做集合级深查找,getXmlSchemas() 返回集合内全部 schema。解析策略由 setSchemaResolver(URIResolver) 注入,可对 xsd:include/xsd:import 的定位做重定向(内网离线场景的标准做法,实测见《Apache XMLSchema 2.3.3 配置参数详解》5.6 节)。

4.2 XmlSchema:单文档模型

read() 返回的 XmlSchema 对应一个 XSD 文档。常用取值方法:getTargetNamespace()(目标命名空间)、getElements()(全局元素 Map)、getSchemaTypes()(全局类型 Map)、getItems()(声明顺序的原始列表)、getElementByName()/getTypeByName()(按名查找)、write()(重新序列化)。

4.3 读取的关键参数:systemId

read(Source) 时必须给 StreamSource 设置 systemId(文件 URI),否则 XSD 里 xsd:include/xsd:import 的相对路径没有解析基准,直接解析失败。这是本库最常见的新手坑,本章示例统一用 xsd.toURI().toString() 设置。

💡 技术拓展:systemId 是 XML 规范里的「系统标识符」,即文档的 URI 基准。JAXP 全家桶(SAXParser、Validator、XmlSchemaCollection)的相对路径解析都依赖它。给法有两种:source.setSystemId(file.toURI().toString())(推荐,跨目录稳定)或集合级 collection.setBaseUri(dir)(给整个集合一个基准目录)。本文用前者。

4.4 线程与可变性

XmlSchemaCollection 内部是普通可变 Map,非线程安全;同一集合跨线程共享时需外部同步或只读使用。2.3.x 可用系统属性 org.apache.ws.commons.schema.protectReadOnlyCollections=true 让返回的集合视图只读化(实测见《Apache XMLSchema 2.3.3 配置参数详解》)。

五、集成示例

本章四组示例可独立执行:先给被解析的 XSD 与实例文档,再逐组给源码、运行命令与完整实测输出。

5.1 示例用 XSD:company.xsd

/root/demo/schemas/company.xsd 内容(编辑器创建):

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:co="http://example.com/company"
           targetNamespace="http://example.com/company"
           elementFormDefault="qualified">

  <!-- 1. 全局简单类型:员工编号,用 pattern 限定格式 -->
  <xs:simpleType name="EmployeeIdType">
    <xs:restriction base="xs:string">
      <xs:pattern value="EMP-[0-9]{4}"/>
    </xs:restriction>
  </xs:simpleType>

  <!-- 2. 全局复杂类型:员工 -->
  <xs:complexType name="EmployeeType">
    <xs:sequence>
      <xs:element name="name" type="xs:string"/>
      <xs:element name="id" type="co:EmployeeIdType"/>
      <xs:element name="salary" type="xs:decimal" minOccurs="0"/>
    </xs:sequence>
    <xs:attribute name="grade" type="xs:int" use="required"/>
  </xs:complexType>

  <!-- 3. 全局元素:employee 引用全局类型 -->
  <xs:element name="employee" type="co:EmployeeType"/>

  <!-- 4. 全局元素:company,匿名复杂类型 + ref 引用 employee -->
  <xs:element name="company">
    <xs:complexType>
      <xs:sequence>
        <xs:element ref="co:employee" maxOccurs="unbounded"/>
      </xs:sequence>
    </xs:complexType>
  </xs:element>

</xs:schema>

配套实例文档 /root/demo/xml/employee-valid.xml(注意属性 grade 不带前缀,原因见 5.3 的坑位说明):

<?xml version="1.0" encoding="UTF-8"?>
<co:company xmlns:co="http://example.com/company">
  <co:employee grade="5">
    <co:name>张三</co:name>
    <co:id>EMP-0001</co:id>
    <co:salary>12000.50</co:salary>
  </co:employee>
  <co:employee grade="3">
    <co:name>李四</co:name>
    <co:id>EMP-0002</co:id>
  </co:employee>
</co:company>

/root/demo/xml/employee-invalid.xml(id 违反 pattern、缺 grade 属性,用于演示校验失败):

<?xml version="1.0" encoding="UTF-8"?>
<co:company xmlns:co="http://example.com/company">
  <co:employee>
    <co:name>王五</co:name>
    <co:id>EMP-12</co:id>
  </co:employee>
</co:company>

5.2 示例一:加载 XSD 并遍历元素/类型/属性

/root/demo/src/main/java/com/example/LoadAndTraverse.java:

package com.example;

import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaAttribute;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaFacet;
import org.apache.ws.commons.schema.XmlSchemaObject;
import org.apache.ws.commons.schema.XmlSchemaParticle;
import org.apache.ws.commons.schema.XmlSchemaSequence;
import org.apache.ws.commons.schema.XmlSchemaSequenceMember;
import org.apache.ws.commons.schema.XmlSchemaSimpleType;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeContent;
import org.apache.ws.commons.schema.XmlSchemaSimpleTypeRestriction;
import org.apache.ws.commons.schema.XmlSchemaType;

import javax.xml.namespace.QName;
import javax.xml.transform.stream.StreamSource;
import java.io.File;
import java.util.Map;

/**
 * E1 主示例:加载 XSD -> XmlSchemaCollection/XmlSchema 对象 -> 遍历元素/类型/属性。
 */
public class LoadAndTraverse {

    public static void main(String[] args) throws Exception {
        File xsd = new File(args[0]);
        XmlSchemaCollection collection = new XmlSchemaCollection();

        // 关键点:StreamSource 必须设置 systemId,xsd:include/xsd:import 的相对路径
        // 才有解析基准;这里用 file URI 指向文件本身
        StreamSource source = new StreamSource(xsd);
        source.setSystemId(xsd.toURI().toString());
        XmlSchema schema = collection.read(source);

        System.out.println("== 1. 模型基本信息 ==");
        System.out.println("targetNamespace      = " + schema.getTargetNamespace());
        System.out.println("logicalTargetNS      = " + schema.getLogicalTargetNamespace());
        System.out.println("elementFormDefault   = " + schema.getElementFormDefault());
        System.out.println("attributeFormDefault = " + schema.getAttributeFormDefault());
        System.out.println("inputEncoding        = " + schema.getInputEncoding());
        System.out.println("getItems().size()    = " + schema.getItems().size());
        System.out.println("collection.getXmlSchemas().length = " + collection.getXmlSchemas().length);

        System.out.println();
        System.out.println("== 2. 全局元素(schema.getElements())==");
        for (Map.Entry<QName, XmlSchemaElement> e : schema.getElements().entrySet()) {
            XmlSchemaElement el = e.getValue();
            System.out.printf("  元素 %-12s 类型=%-30s 匿名类型=%s nillable=%s%n",
                    e.getKey(), String.valueOf(el.getSchemaTypeName()),
                    el.getSchemaType() != null && el.getSchemaTypeName() == null,
                    el.isNillable());
        }

        System.out.println();
        System.out.println("== 3. 全局类型(schema.getSchemaTypes())==");
        for (Map.Entry<QName, XmlSchemaType> t : schema.getSchemaTypes().entrySet()) {
            System.out.println("  类型 " + t.getKey() + " -> " + t.getValue().getClass().getSimpleName());
        }

        System.out.println();
        System.out.println("== 4. 集合级查找 API(深查找,覆盖 import 进来的 schema)==");
        QName empQ = new QName(schema.getTargetNamespace(), "employee");
        XmlSchemaElement found = collection.getElementByQName(empQ);
        System.out.println("collection.getElementByQName(employee) = " + found.getName()
                + "(类型 " + found.getSchemaTypeName() + ")");
        XmlSchemaType empType = collection.getTypeByQName(
                new QName(schema.getTargetNamespace(), "EmployeeType"));
        System.out.println("collection.getTypeByQName(EmployeeType) = " + empType.getClass().getSimpleName());

        System.out.println();
        System.out.println("== 5. 复杂类型深遍历(sequence 子元素 / 属性 / 简单类型约束)==");
        walkType(schema, "EmployeeType");
        walkType(schema, "EmployeeIdType");

        System.out.println();
        System.out.println("== 6. 匿名复杂类型(company 元素内联定义)==");
        XmlSchemaElement company = schema.getElementByName("company");
        XmlSchemaComplexType anon = (XmlSchemaComplexType) company.getSchemaType();
        System.out.println("company.getSchemaType() = " + anon.getClass().getSimpleName()
                + ",匿名类型 QName = " + anon.getQName());
        XmlSchemaParticle p = anon.getParticle();
        XmlSchemaSequence seq = (XmlSchemaSequence) p;
        for (XmlSchemaSequenceMember o : seq.getItems()) {
            XmlSchemaElement ref = (XmlSchemaElement) o;
            System.out.printf("  sequence 子项 name=%-9s ref指向=%-40s maxOccurs=%d%n",
                    ref.getName(), String.valueOf(ref.getTargetQName()), ref.getMaxOccurs());
        }
    }

    /** 按「序列子元素 + 属性」两层遍历复杂类型;简单类型则打印 restriction 约束面。 */
    static void walkType(XmlSchema schema, String typeName) {
        XmlSchemaType type = schema.getTypeByName(typeName);
        System.out.println("[" + typeName + "] " + type.getClass().getSimpleName());
        if (type instanceof XmlSchemaComplexType) {
            XmlSchemaComplexType ct = (XmlSchemaComplexType) type;
            XmlSchemaParticle particle = ct.getParticle();
            if (particle instanceof XmlSchemaSequence) {
                XmlSchemaSequence s = (XmlSchemaSequence) particle;
                // 注意:getItems() 返回 List<XmlSchemaSequenceMember>(元素/any 的公共接口),
                // 需要转成 XmlSchemaElement 才能取元素属性
                for (XmlSchemaSequenceMember o : s.getItems()) {
                    XmlSchemaElement el = (XmlSchemaElement) o;
                    System.out.printf("    序列子元素 %-9s 类型=%-28s minOccurs=%d maxOccurs=%d%n",
                            el.getName(), String.valueOf(el.getSchemaTypeName()),
                            el.getMinOccurs(), el.getMaxOccurs());
                }
            }
            for (XmlSchemaObject o : ct.getAttributes()) {
                XmlSchemaAttribute at = (XmlSchemaAttribute) o;
                System.out.printf("    属性       %-8s use=%-9s 类型=%s%n",
                        at.getName(), at.getUse(), at.getSchemaTypeName());
            }
        } else if (type instanceof XmlSchemaSimpleType) {
            XmlSchemaSimpleType st = (XmlSchemaSimpleType) type;
            XmlSchemaSimpleTypeContent content = st.getContent();
            if (content instanceof XmlSchemaSimpleTypeRestriction) {
                XmlSchemaSimpleTypeRestriction r = (XmlSchemaSimpleTypeRestriction) content;
                System.out.println("    restriction base=" + r.getBaseTypeName());
                for (XmlSchemaFacet f : r.getFacets()) {
                    System.out.println("    约束面 " + f.getClass().getSimpleName()
                            + " value=" + f.getValue());
                }
            }
        }
    }
}

运行:

mvn -q exec:java -Dexec.mainClass=com.example.LoadAndTraverse -Dexec.args='schemas/company.xsd'

实测输出(完整):

== 1. 模型基本信息 ==
targetNamespace      = http://example.com/company
logicalTargetNS      = http://example.com/company
elementFormDefault   = qualified
attributeFormDefault = unqualified
inputEncoding        = UTF-8
getItems().size()    = 4
collection.getXmlSchemas().length = 2

== 2. 全局元素(schema.getElements())==
  元素 {http://example.com/company}employee 类型={http://example.com/company}EmployeeType 匿名类型=false nillable=false
  元素 {http://example.com/company}company 类型=null                           匿名类型=true nillable=false

== 3. 全局类型(schema.getSchemaTypes())==
  类型 {http://example.com/company}EmployeeIdType -> XmlSchemaSimpleType
  类型 {http://example.com/company}EmployeeType -> XmlSchemaComplexType

== 4. 集合级查找 API(深查找,覆盖 import 进来的 schema)==
collection.getElementByQName(employee) = employee(类型 {http://example.com/company}EmployeeType)
collection.getTypeByQName(EmployeeType) = XmlSchemaComplexType

== 5. 复杂类型深遍历(sequence 子元素 / 属性 / 简单类型约束)==
[EmployeeType] XmlSchemaComplexType
    序列子元素 name      类型={http://www.w3.org/2001/XMLSchema}string minOccurs=1 maxOccurs=1
    序列子元素 id        类型={http://example.com/company}EmployeeIdType minOccurs=1 maxOccurs=1
    序列子元素 salary    类型={http://www.w3.org/2001/XMLSchema}decimal minOccurs=0 maxOccurs=1
    属性       grade    use=required  类型={http://www.w3.org/2001/XMLSchema}int
[EmployeeIdType] XmlSchemaSimpleType
    restriction base={http://www.w3.org/2001/XMLSchema}string
    约束面 XmlSchemaPatternFacet value=EMP-[0-9]{4}

== 6. 匿名复杂类型(company 元素内联定义)==
company.getSchemaType() = XmlSchemaComplexType,匿名类型 QName = null
  sequence 子项 name=null      ref指向={http://example.com/company}employee     maxOccurs=9223372036854775807

结果解读:输出里有三个值得注意的真实行为。其一,getXmlSchemas().length = 2——只读了一个文件却是 2 个 schema,因为集合初始化时预建了「XML Schema 命名空间自身」的模型对象;其二,company 元素类型为 null 但匿名类型为 true,即内联定义的类型没有 QName,必须走 getSchemaType() 拿对象;其三,maxOccurs=9223372036854775807 就是 xs:unbounded 的 long 表示(Long.MAX_VALUE)。

5.3 示例二:与 JAXP 联动校验实例文档

xmlschema-core 只建模 XSD,实例文档校验交给 JDK 自带的 JAXP Validator。/root/demo/src/main/java/com/example/JaxpValidate.java:

package com.example;

import org.xml.sax.SAXParseException;

import javax.xml.XMLConstants;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;
import javax.xml.validation.Validator;
import java.io.File;

/**
 * E1 联动示例:XmlSchemaCollection 只建模信息集,实例文档校验交给 JAXP Validator。
 */
public class JaxpValidate {

    public static void main(String[] args) throws Exception {
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema jaxpSchema = sf.newSchema(new File(args[0]));
        Validator validator = jaxpSchema.newValidator();

        try {
            validator.validate(new StreamSource(new File(args[1])));
            System.out.println("校验通过:" + args[1] + " 符合 " + args[0]);
        } catch (SAXParseException e) {
            System.out.println("校验失败:" + args[1]);
            System.out.println("  位置:第 " + e.getLineNumber() + " 行,第 " + e.getColumnNumber() + " 列");
            System.out.println("  原因:" + e.getMessage());
        }
    }
}

先校验合法文档:

mvn -q exec:java -Dexec.mainClass=com.example.JaxpValidate -Dexec.args='schemas/company.xsd xml/employee-valid.xml'

实测输出:

校验通过:xml/employee-valid.xml 符合 schemas/company.xsd

再校验非法文档:

mvn -q exec:java -Dexec.mainClass=com.example.JaxpValidate -Dexec.args='schemas/company.xsd xml/employee-invalid.xml'

实测输出:

校验失败:xml/employee-invalid.xml
  位置:第 3 行,第 16 列
  原因:cvc-complex-type.4: Attribute 'grade' must appear on element 'co:employee'.

⚠️ 注意(实测踩坑):最初实例文档里把属性写成 co:grade="5",校验报 cvc-complex-type.3.2.2: Attribute 'co:grade' is not allowed to appear in element 'co:employee'。原因是 XSD 的 elementFormDefault="qualified" 只约束元素,属性默认 attributeFormDefault="unqualified",所以实例中属性必须裸写 grade="5"。要么改实例(本文做法),要么在 XSD 上显式声明 attributeFormDefault="qualified"。

5.4 示例三:对象级建模(从零构造 XSD)

不读文件,直接用对象模型构造一个 XSD 并序列化输出。/root/demo/src/main/java/com/example/BuildSchema.java:

package com.example;

import org.apache.ws.commons.schema.XmlSchema;
import org.apache.ws.commons.schema.XmlSchemaCollection;
import org.apache.ws.commons.schema.XmlSchemaComplexType;
import org.apache.ws.commons.schema.XmlSchemaElement;
import org.apache.ws.commons.schema.XmlSchemaForm;
import org.apache.ws.commons.schema.XmlSchemaSequence;

import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import java.io.ByteArrayOutputStream;

/**
 * E1 补充示例:用对象模型从零构造一个 XSD(编程建模),并序列化为 XML。
 */
public class BuildSchema {

    public static void main(String[] args) throws Exception {
        XmlSchemaCollection collection = new XmlSchemaCollection();

        // 1. 在集合上创建一个新 schema(targetNamespace 编程指定)
        XmlSchema schema = new XmlSchema("http://example.com/built", collection);
        schema.setElementFormDefault(XmlSchemaForm.QUALIFIED);

        // 2. 全局复杂类型 GreetingType:一个 sequence,只有一个 name 子元素
        // 注意:topLevel=true 时构造器已自动把类型注册进 schema,无需再手动 add
        XmlSchemaComplexType type = new XmlSchemaComplexType(schema, true);
        type.setName("GreetingType");
        XmlSchemaSequence seq = new XmlSchemaSequence();
        XmlSchemaElement name = new XmlSchemaElement(schema, false);
        name.setName("name");
        name.setSchemaTypeName(new QName(XMLConstants.W3C_XML_SCHEMA_NS_URI, "string"));
        name.setMinOccurs(1);
        name.setMaxOccurs(1);
        seq.getItems().add(name);
        type.setParticle(seq);

        // 3. 全局元素 greeting 引用该类型(topLevel=true,同样自动注册)
        XmlSchemaElement greeting = new XmlSchemaElement(schema, true);
        greeting.setName("greeting");
        greeting.setSchemaTypeName(new QName("http://example.com/built", "GreetingType"));

        // 4. 序列化输出
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        schema.write(bos);
        System.out.println(new String(bos.toByteArray(), "UTF-8"));
    }
}

运行:

mvn -q exec:java -Dexec.mainClass=com.example.BuildSchema

实测输出(完整):

<schema xmlns="http://www.w3.org/2001/XMLSchema" xmlns:tns="http://example.com/built" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://example.com/built">
    <complexType name="GreetingType">
        <sequence>
            <element name="name" type="string"/>
        </sequence>
    </complexType>
    <element name="greeting" type="tns:GreetingType"/>
</schema>

⚠️ 注意(实测踩坑):构件构造器第二个参数 topLevel 传 true 时,对象会自动注册进 schema 的全局列表;若再手动 schema.getItems().add(...),序列化输出中该构件会出现两次(实测复现过:complexType 与 element 各输出两份)。全局构件一律 topLevel=true 且不再手动 add。

六、注意事项

  1. 中文输出乱码排障:默认 locale 非 UTF-8 的会话里,示例输出的中文变成问号(实测复现:== 1. ?????? ==)。解决:export LANG=C.UTF-8 LC_ALL=C.UTF-8 后重新运行。若用 systemd 托管自己的应用,同样在 Environment= 中注入 LANG=C.UTF-8。
  2. systemId 必须设置:所有 read(Source) 场景都要 source.setSystemId(...),否则含相对 include/import 的 XSD 直接解析失败。
  3. 序列成员的接口类型:XmlSchemaSequence.getItems() 返回 List<XmlSchemaSequenceMember>,按 List<XmlSchemaObject> 接收会编译失败(实测报错 incompatible types: XmlSchemaSequenceMember cannot be converted to XmlSchemaObject),需以成员接口迭代后强转 XmlSchemaElement。
  4. 重复全局元素读取不报错:xmlschema-core 读取时后声明覆盖前声明;同样的 XSD 交给 JAXP SchemaFactory.newSchema() 编译才报 sch-props-correct.2。两个工具的容错语义不同,见《Apache XMLSchema 2.3.3 配置参数详解》对照实测。
  5. 校验职责分工:建模用 XmlSchemaCollection,实例校验用 JAXP Validator;不要试图用前者校验 XML 实例文档。
  6. 版本对齐:与 CXF/Axis2 同工程时,用 dependencyManagement 统一 xmlschema-core 到 2.3.3(CXF 4.1.4 默认传 2.3.2、Axis2 1.8.2 默认传 2.3.0,实测自两者父 POM)。

七、参考链接