Apache Daffodil 快速入门与集成示例
Apache Daffodil 4.2.0 快速入门与集成示例,从环境准备到首个 DFDL 解析/回写示例逐步演示,并给出 Java API 与 Maven 依赖集成、以及在数据处理流程中嵌入 Daffodil 的样例代码与真实运行结果。
Apache Daffodil 快速入门与集成示例
目标:从零建立对 Apache Daffodil 的完整认知——先用 CLI 跑通一个定长文本的解析与回写,再通过 Java API 与 Maven 把 Daffodil 嵌入到数据处理流程中。所有命令、代码与回显均基于 Daffodil 4.2.0 实测。
一、快速入门目标与环境准备
1.1 你将完成
- 用 DFDL schema 描述一个定长文本记录。
- 用
daffodil parse把数据解析为 XML/JSON 信息集。 - 用
daffodil unparse把信息集回写为原始数据,并验证字节一致。 - 用 Java API + Maven 在应用中集成 Daffodil。
1.2 环境准备
已安装 JDK 17 与 Daffodil 4.2.0(安装位置 /opt/daffodil),并配置:
export JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64
export DAFFODIL_HOME=/opt/daffodil
export PATH=$JAVA_HOME/bin:$DAFFODIL_HOME/bin:$PATH
验证:
daffodil --version
真实回显:
Apache Daffodil 4.2.0
Daffodil 4.x 需 JDK 17。所有示例工作目录为
/root/dfdl。
二、第一个 DFDL 例子
2.1 编写 schema
创建 person.dfdl.xsd:一条定长文本记录,姓名 5 字符、年龄 3 字符、编号 2 字符。
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:dfdl="http://www.ogf.org/dfdl/dfdl-1.0/"
targetNamespace="http://example.com/dfdl/person"
xmlns:tns="http://example.com/dfdl/person"
elementFormDefault="unqualified">
<xs:include schemaLocation="/org/apache/daffodil/xsd/DFDLGeneralFormat.dfdl.xsd"/>
<xs:annotation>
<xs:appinfo source="http://www.ogf.org/dfdl/">
<dfdl:format ref="tns:GeneralFormat" encoding="ASCII" representation="text"/>
</xs:appinfo>
</xs:annotation>
<xs:element name="person" type="tns:PersonType" dfdl:lengthKind="implicit"/>
<xs:complexType name="PersonType">
<xs:sequence>
<xs:element name="name" type="xs:string"
dfdl:lengthKind="explicit" dfdl:length="5" dfdl:lengthUnits="characters"/>
<xs:element name="age" type="xs:unsignedInt"
dfdl:lengthKind="explicit" dfdl:length="3" dfdl:lengthUnits="characters"
dfdl:textNumberPattern="000"/>
<xs:element name="code" type="xs:string"
dfdl:lengthKind="explicit" dfdl:length="2" dfdl:lengthUnits="characters"/>
</xs:sequence>
</xs:complexType>
</xs:schema>
2.2 准备数据
创建 person.txt(共 10 字符):
Alice025AB
字段切分:name=Alice(5) / age=025(3) / code=AB(2)。
2.3 解析
cd /root/dfdl
daffodil parse -s person.dfdl.xsd person.txt
真实回显(exit=0):
<?xml version="1.0" encoding="UTF-8"?>
<tns:person xmlns:tns="http://example.com/dfdl/person">
<name>Alice</name>
<age>25</age>
<code>AB</code>
</tns:person>
2.4 解读信息集
- 输出是 DFDL 信息集(infoset),根元素为
tns:person,命名空间来自 schema 的targetNamespace。 name、code为字符串;age为无类型值的十进制数(025→25)。- 信息集 XML 即为 Daffodil 的标准输入/输出格式,可直接被
unparse消费。
三、unparse 回写闭环
daffodil parse -s person.dfdl.xsd person.txt -o person.xml
daffodil unparse -s person.dfdl.xsd person.xml -o person.out
od -c person.out
cmp person.txt person.out && echo IDENTICAL
真实回显:
0000000 A l i c e 0 2 5 A B
0000012
IDENTICAL
要点:age 字段因 dfdl:textNumberPattern="000" 采用零填充,回写后恢复为 025,从而与原始数据字节级一致。
四、输出 JSON 信息集
daffodil parse -s person.dfdl.xsd -I json person.txt
真实回显:
{
"person": {
"name": "Alice",
"age": 25,
"code": "AB"
}
}
五、Java API 集成
5.1 Maven 依赖坐标
Daffodil 4.x 的 Java API 类(org.apache.daffodil.api.*)由 daffodil-core_3 提供(经 Maven Central 校验,daffodil-core_3-4.2.0.jar 含 org/apache/daffodil/api/Daffodil.class):
<dependency>
<groupId>org.apache.daffodil</groupId>
<artifactId>daffodil-core_3</artifactId>
<version>4.2.0</version>
</dependency>
完整 pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0">
<modelVersion>4.0.0</modelVersion>
<groupId>com.example</groupId>
<artifactId>daffodil-demo</artifactId>
<version>1.0.0</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.apache.daffodil</groupId>
<artifactId>daffodil-core_3</artifactId>
<version>4.2.0</version>
</dependency>
</dependencies>
</project>
5.2 样例代码
src/main/java/DaffodilDemo.java:
import java.io.File;
import java.io.FileInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.channels.Channels;
import org.apache.daffodil.api.Compiler;
import org.apache.daffodil.api.Daffodil;
import org.apache.daffodil.api.DataProcessor;
import org.apache.daffodil.api.Diagnostic;
import org.apache.daffodil.api.InputSourceDataInputStream;
import org.apache.daffodil.api.ParseResult;
import org.apache.daffodil.api.ProcessorFactory;
import org.apache.daffodil.api.UnparseResult;
import org.apache.daffodil.api.infoset.InfosetInputter;
import org.apache.daffodil.api.infoset.InfosetOutputter;
public class DaffodilDemo {
public static void main(String[] args) throws Exception {
File schemaFile = new File("/root/dfdl/person.dfdl.xsd");
File dataFile = new File("/root/dfdl/person.txt");
File infosetFile = new File("/root/dfdl/person.xml");
// 1) 编译 schema 得到 ProcessorFactory
Compiler compiler = Daffodil.compiler();
ProcessorFactory pf = compiler.compileFile(schemaFile);
if (pf.isError()) {
for (Diagnostic d : pf.getDiagnostics()) {
System.out.println("[schema] " + d.getMessage());
}
System.exit(1);
}
DataProcessor dp = pf.onPath("/");
// 2) 解析数据 → XML 信息集
InputSourceDataInputStream in =
Daffodil.newInputSourceDataInputStream(new FileInputStream(dataFile));
InfosetOutputter out = Daffodil.newXMLTextInfosetOutputter(System.out, true);
ParseResult pr = dp.parse(in, out);
System.out.println();
System.out.println("parse isProcessingError=" + pr.isProcessingError());
// 3) 回写信息集 → 原始数据
ByteArrayOutputStream bos = new ByteArrayOutputStream();
InfosetInputter inp =
Daffodil.newXMLTextInfosetInputter(new FileInputStream(infosetFile));
UnparseResult ur = dp.unparse(inp, Channels.newChannel(bos));
System.out.println("unparse isProcessingError=" + ur.isProcessingError());
System.out.println("unparse output bytes: ["
+ new String(bos.toByteArray(), "US-ASCII") + "]");
}
}
5.3 编译与运行(两种方式)
方式一:直接用发行版 jar(无需 Maven):
javac -cp "/opt/daffodil/lib/*" -d . DaffodilDemo.java
java -cp ".:/opt/daffodil/lib/*" DaffodilDemo
方式二:Maven 构建:
mvn -B -q compile org.codehaus.mojo:exec-maven-plugin:3.1.0:java -Dexec.mainClass=DaffodilDemo
真实回显(两种方式输出一致,exit=0):
=== parse data -> infoset (XML) ===
<?xml version="1.0" encoding="UTF-8"?>
<tns:person xmlns:tns="http://example.com/dfdl/person">
<name>Alice</name>
<age>25</age>
<code>AB</code>
</tns:person>
parse isProcessingError=false
parse isValidationError=false
=== unparse infoset -> data ===
unparse isProcessingError=false
unparse output bytes: [Alice025AB]
六、Scala API 参考写法
Daffodil 的 Java API 与 Scala API 共用同一套 org.apache.daffodil.api 类型(发行 jar 为 Scala 3 编译)。以下 Scala 写法与上文 Java 逻辑一一对应,供 Scala 工程参考:
import java.io.{ByteArrayOutputStream, File, FileInputStream}
import java.nio.channels.Channels
import org.apache.daffodil.api.Daffodil
object DaffodilDemoScala {
def main(args: Array[String]): Unit = {
val compiler = Daffodil.compiler()
val pf = compiler.compileFile(new File("/root/dfdl/person.dfdl.xsd"))
if (pf.isError()) sys.error("schema error")
val dp = pf.onPath("/")
val in = Daffodil.newInputSourceDataInputStream(new FileInputStream("/root/dfdl/person.txt"))
val out = Daffodil.newXMLTextInfosetOutputter(System.out, true)
dp.parse(in, out)
val bos = new ByteArrayOutputStream()
val inp = Daffodil.newXMLTextInfosetInputter(new FileInputStream("/root/dfdl/person.xml"))
dp.unparse(inp, Channels.newChannel(bos))
println("unparse bytes: [" + new String(bos.toByteArray, "US-ASCII") + "]")
}
}
说明:Java API 已在本环境实测通过;Scala 写法为同一 API 的等价调用,需在含 Scala 3 运行时的工程中编译。
七、在数据处理流程中嵌入 Daffodil
典型 ETL/网关场景:把定长报文解析为结构化信息集后进入下游处理,再按需回写。
// 每收到一批报文,复用同一个 DataProcessor(避免重复编译 schema)
ProcessorFactory pf = Daffodil.compiler().compileFile(new File("/opt/schemas/msg.dfdl.xsd"));
DataProcessor dp = pf.onPath("/");
for (File record : inbox) {
ByteArrayOutputStream xml = new ByteArrayOutputStream();
InfosetOutputter out = Daffodil.newXMLTextInfosetOutputter(xml, true);
dp.parse(Daffodil.newInputSourceDataInputStream(new FileInputStream(record)), out);
// 交给下游(校验、入库、转发…)
process(xml.toByteArray());
// 需要时再回写为原始格式
ByteArrayOutputStream raw = new ByteArrayOutputStream();
dp.unparse(
Daffodil.newXMLTextInfosetInputter(new java.io.ByteArrayInputStream(xml.toByteArray())),
Channels.newChannel(raw));
archive(raw.toByteArray());
}
性能与稳定性建议:
- 复用
DataProcessor:compileFile只做一次;CLI 侧也可用save-parser生成 parser 文件后用-P复用。 - 限制堆内存:
export DAFFODIL_JAVA_OPTS="-Xms256m -Xmx512m"。 - 用 tunable 兜底:如
maxOccursBounds、maxFieldContentLengthInBytes防止异常数据导致内存激增。 - 开启校验:
-V daffodil或 APIwithValidation("daffodil")在解析阶段暴露数据问题。
八、常见问题
Q1:Java API 报 UnsupportedClassVersionError ... class file version 61.0?
运行环境 JDK 低于 17。Daffodil 4.x 需 JDK 17,升级 JDK 或指定 JAVA_HOME。
Q2:compileFile 返回的 pf.isError() 为 true?
schema 有定义错误。遍历 pf.getDiagnostics() 打印诊断;常见原因是缺少 DFDLGeneralFormat 引用(报 Property ... is not defined)。
Q3:parse 报 Parse Error: Unable to parse xs:unsignedInt from text?
数据与 schema 字段切分不匹配。核对 dfdl:length 与真实数据,注意数值字段需 dfdl:textNumberPattern 才能在回写时保留前导零。
Q4:Maven 找不到 daffodil-japi_3?
Daffodil 4.x 未发布 daffodil-japi_3;Java API 类包含在 org.apache.daffodil:daffodil-core_3:4.2.0 中,直接依赖该坐标即可。