7 Efficient XML编解码库
欧洲的 http://www.agiledelta.com公司是EXI协议发起者,也推出了一套EXI编解码库(Efficient XML)。作者联系agiledelta公司,获取了一套试用版本软件,进行了编解码测试。 测试结果表明针对ISO15118-2/20的V2G命令,大部分编解码结果是与EXICodec.jar工具一致,少部分与签名相关命令编码结果不同,不能达到互通。本节详细对Efficient XML的研究和测试过程。
7.1 EfficientXML试用版本下载、安装、激活
1价格
Efficient XML是一套收费版本,通过邮件沟通费用如下。
1 产品许可证随数量分3档:
10K $44000, $4.40/per
100K $88000, $0.88/per
1M $176000, $0.176/per
2 开发人员许可证SDK,提供一些工具(例如预处理schema的编译器)。每个SDK $1314.5。
第一年免费支持(邮件、网络),第二年开始收费为20%($262.9/per)。
3 对每个平台收取移植费用,通常在$5,000- 10,000美元。
说实话,这个编解码库挺贵的,尤其是必须针对开发人员收取许可证SDK,对平台还要移植费用。
2下载试用版本
通过邮件沟通后拿到了下载地址和密钥,这个试用期版本有效期只有1月,现在已经过期了,公布在这里没有关系。
下载地址: http://www.agiledelta.com/efxsup_download.html
密钥:CT3JPJ-48JDW-HYF5T-32CW4-R2VVH
下载页面要注意使用最新日期的版本:
Efficient XML for C/C++, Linux Edition Version 5.3.0 516 KBytes Released 06/23/2023 这个是正确的版本
3 激活方法
在linux下解压软件包,执行许可向导:
root@Tom-Hongtao:/opt/efxc5-linux# tar -zxvf efxc530-Linux.tar.gz
root@Tom-Hongtao:/opt/efxc5-linux# cd EfxC/bin
root@Tom-Hongtao:/opt/efxc5-linux/EfxC/bin# bash ./license_wizard.sh
【注意】这个向导需要java支持,将会运行jar包,将会针对本机器生成一个激活码,以后只能在这台机器上运行软件。
弹出窗口, 黏贴邮件中的密钥即可:
点击"Next",
就用第一项,点击"Next", 注意这里出现了机器id,就是本电脑的id,意味着将要生成的许可密钥是针对本机的。
点击"Next",出现激活成功! 这里出现的LicenseKey已经是本机的密钥了。
点击"Next",出现许可密钥的使用方法:
就是需要在linxu中设置环境变量:
root@Tom-Hongtao:/opt/efxc5-linux/EfxC/bin# export EFX_LICENSE=3SAEEN-AAMDC-UFB48-73IDD-SF7JG
如果要作为系统环境变量就要写入profile:
root@Tom-Hongtao:/etc# vi profile
#在最后一行写入
export EFX_LICENSE=3SAEEN-AAMDC-UFB48-73IDD-SF7JG
root@Tom-Hongtao:/etc# source /etc/profile
这样以后就一劳永逸了,每次打开终端就能查看到这个环境变量:
打开一个新终端:
tom@Tom-Hongtao:~$ export
declare -x CLASSPATH=".:/usr/lib/jvm/java-17-openjdk-amd64/lib:/usr/lib/jvm/java-17-openjdk-amd64/jre/lib"
declare -x DBUS_SESSION_BUS_ADDRESS="unix:path=/run/user/1000/bus"
declare -x DISPLAY=":0"
declare -x EFX_LICENSE="3SAEEN-AAMDC-UFB48-73IDD-SF7JG"
declare -x HOME="/home/tom"
如果没有正确设置环境变量,在运行相关程序时就会出现错误提示:
root@Tom-Hongtao:/opt/efxc5-linux/EfxC/samples# ./reader
Error detected: Unable to obtain Efficient XML license key (EFX_LICENSE) from environment. Set key in environment and try again.
7.2 编译示例程序
Efficient XML库提供了核心的编解码库,并不包含对应的C源码文件,目录内容如下:
bin: 许可向导脚本
doc:
include: 编解码用到的函数接口
lib: efxact.jar,核心的编解码动态链接库
samples: 示例代码,包含编解码程序
schema: 通过专用的schema工具把xsd转换成cxs文件。试用版本不提供这个工具,需要联系购买。
x32: 在PC平台上的库
查看lib下的库文件,
tom@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/lib$ ls -l
总计 512
-rwxrwxrwx 1 tom tom 48364 8月 30 11:25 efxact.jar
-rwxrwxrwx 1 tom tom 170752 8月 30 11:25 libefx.5.3.0.a
lrwxrwxrwx 1 tom tom 15 8月 30 11:25 libefx.so -> libefx.so.5.3.0
-rwxrwxrwx 1 tom tom 154336 8月 30 11:25 libefx.so.5.3.0
-rwxrwxrwx 1 tom tom 14218 8月 30 11:25 libxml2-efx.5.3.0.a
lrwxrwxrwx 1 tom tom 20 8月 30 11:25 libxml2-efx.so -> libxml2-efx.so.5.3.0
-rwxrwxrwx 1 tom tom 18912 8月 30 11:25 libxml2-efx.so.5.3.0
-rwxrwxrwx 1 tom tom 21428 8月 30 11:25 libxmlreader.5.3.0.a
-rwxrwxrwx 1 tom tom 14712 8月 30 11:25 libxmlreader-efx.5.3.0.a
lrwxrwxrwx 1 tom tom 25 8月 30 11:25 libxmlreader-efx.so -> libxmlreader-efx.so.5.3.0
-rwxrwxrwx 1 tom tom 14784 8月 30 11:25 libxmlreader-efx.so.5.3.0
lrwxrwxrwx 1 tom tom 21 8月 30 11:25 libxmlreader.so -> libxmlreader.so.5.3.0
-rwxrwxrwx 1 tom tom 22536 8月 30 11:25 libxmlreader.so.5.3.0
-rwxrwxrwx 1 tom tom 10828 8月 30 11:25 libxmlwriter.5.3.0.a
lrwxrwxrwx 1 tom tom 21 8月 30 11:25 libxmlwriter.so -> libxmlwriter.so.5.3.0
-rwxrwxrwx 1 tom tom 14320 8月 30 11:25 libxmlwriter.so.5.3.0
tom@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/lib$
进入sample目录,包含这些示例:
encode.c - transcode EXI to XML
decode.c - transcode XML to EXI
writer.c - uses EFXWriter APIs to write a EXI message (more efficient than transcoder in encode.c)
reader.c - uses EFXReader APIs to read a EXI message (more efficient than transcoder in decode.c)
schemaresolver.c - shows how to use a schema resolve to dynamically set a schema
io-file.c - shows how to perform I/O with files
io-buffer.c - shows how to perform I/O with buffers
flightdata.cxs - compiled schema used in the examples
flightdata.xsd - XML Schema source for the compiled schema
flightdata.xml - sample XML message
flightdata.xml.exi - EXI encoded version of the sample message
查看Makefile文件,编译所有示例程序:
tom@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/samples$ make
gcc -o reader -O -std=c99 -w -I../include -L../lib reader.c -lefx -lz
gcc -o writer -O -std=c99 -w -I../include -L../lib writer.c -lefx -lz
gcc -o encode -O -std=c99 -w -I../include -L../lib encode.c -lefx -lz -lxmlreader-efx -lxmlwriter -lxmlreader
gcc -o decode -O -std=c99 -w -I../include -L../lib decode.c -lefx -lz -lxmlreader-efx -lxmlwriter -lxmlreader
gcc -o io-buffer -O -std=c99 -w -I../include -L../lib io-buffer.c -lefx -lz -lxmlreader-efx -lxmlwriter -lxmlreader
gcc -o io-file -O -std=c99 -w -I../include -L../lib io-file.c -lefx -lz -lxmlreader-efx -lxmlwriter -lxmlreader
gcc -o set-string-table -O -std=c99 -w -I../include -L../lib set-string-table.c -lefx -lz
gcc -o string-intern -O -std=c99 -w -I../include -L../lib string-intern.c -lefx -lz
gcc -o dtrm-guid -O -std=c99 -w -I../include -L../lib dtrm-guid.c -lefx -lz -lxmlreader-efx -lxmlwriter -lxmlreader
gcc -o dtrm-list -O -std=c99 -w -I../include -L../lib dtrm-list.c -lefx -lz -lxmlreader-efx -lxmlwriter -lxmlreader
gcc -o schemaresolver -O -std=c99 -w -I../include -L../lib schemaresolver.c -lefx -lz
gcc -o settings-file-use -O -std=c99 -w -I../include -L../lib settings-file-use.c -lefx -lz -lxmlreader-efx -lxmlwriter -lxmlreader
tom@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/samples$
随便挑一个程序来运行,会出现这样的错误提示:
error while loading shared libraries: libefx.so:cannot open shared object file: No such file or directory
这时因为还没有在系统中声明这些lib库的路径,程序傻傻的找不到。
7.3 设置动态链接库路径
现在我们需要用root用户进行操作,为系统设置正确的动态链接库路径。
一般用户自己的库都存放到 /usr/local/lib下面, 我们把efficientXML的这些lib拷贝到这个目录
root@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/lib# cp -a lib*.* /usr/local/lib/efficientXML/
root@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/lib#
要把这个路径添加到动态链接库搜索路径中。
linux系统默认的库搜索路径是/lib和/usr/lib目录,要增加一个搜索路径需要修改动态链接库配置文件:
root@Tom-Hongtao:/usr/local/lib# vi /etc/ld.so.conf
include /etc/ld.so.conf.d/*.conf
/usr/local/lib/efficientXML #添加这一个路径
#更新配置
root@Tom-Hongtao:/usr/local/lib# ldconfig
/sbin/ldconfig.real: /usr/lib/wsl/lib/libcuda.so.1 不是符号链接
root@Tom-Hongtao:/usr/local/lib#
现在就大功告成了,随意运行程序都能找到efficientXML的这些动态库了。
查看应用程序使用的动态库
root@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/samples# ldd reader
linux-vdso.so.1 (0x00007ffda4de9000)
libefx.so => /usr/local/lib/efficientXML/libefx.so (0x00007ff37c600000)
libz.so.1 => /lib/x86_64-linux-gnu/libz.so.1 (0x00007ff37c903000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007ff37c3d8000)
/lib64/ld-linux-x86-64.so.2 (0x00007ff37c930000)
root@Tom-Hongtao:/mnt/c/E/codes/EfficientXML/EfxC/samples#
从上面就能看出来libefx.so指向了我们刚刚配置的动态库路径,OK!!!
7.4 分析samples程序
本节分析编解码程序,说明关键的库函数调用方法。
7.4.1 示例使用的xml文件
flightdata.xml
<?xml version="1.0" encoding="UTF-8"?>
<p0:dailyFlightInfo xmlns:p0="http://www.example.com/flightdata">
<day>2003-10-10</day>
<flightInfo>
<airline>Alaska</airline>
<departure>
<airport>LAX</airport>
<time>
<hour>7</hour>
<minute>35</minute>
</time>
</departure>
<arrival>
<airport>SEA</airport>
<time>
<hour>9</hour>
<minute>15</minute>
<status>ON-TIME</status>
</time>
</arrival>
</flightInfo>
<flightInfo>
<airline>United</airline>
<departure>
<airport>SFO</airport>
<time>
<hour>19</hour>
<minute>40</minute>
</time>
</departure>
<arrival>
<airport>BSO</airport>
<time>
<hour>22</hour>
<minute>25</minute>
<status>CANCELLED</status>
</time>
</arrival>
</flightInfo>
</p0:dailyFlightInfo>
flightdata.xsd:
<?xml version="1.0" ?>
<xs:schema targetNamespace="http://www.example.com/flightdata"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:ns="http://www.example.com/flightdata" >
<xs:element name="dailyFlightInfo">
<xs:complexType>
<xs:sequence>
<xs:element name="day" type="xs:date" />
<xs:element name="flightInfo" type="ns:FlightInfoType" maxOccurs="unbounded" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:complexType name="FlightInfoType">
<xs:sequence>
<xs:element name="airline" type="xs:NCName" />
<xs:element name="departure" type="ns:FlightType" />
<xs:element name="arrival" type="ns:FlightType" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="FlightType">
<xs:sequence>
<xs:element name="airport" type="ns:AirportType" />
<xs:element name="time" type="ns:TimeType" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="AirportType">
<xs:restriction base="xs:NCName">
<xs:pattern value="[A-Z]{3}" />
</xs:restriction>
</xs:simpleType>
<xs:complexType name="TimeType">
<xs:sequence>
<xs:element name="hour" type="ns:HourType" />
<xs:element name="minute" type="ns:MinuteType" />
<xs:element name="status" type="ns:StatusType" minOccurs="0" />
</xs:sequence>
</xs:complexType>
<xs:simpleType name="StatusType">
<xs:restriction base="xs:NCName">
<xs:enumeration value="ON-TIME" />
<xs:enumeration value="DELAYED" />
<xs:enumeration value="CANCELLED" />
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="HourType">
<xs:restriction base="xs:int">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="23"/>
</xs:restriction>
</xs:simpleType>
<xs:simpleType name="MinuteType">
<xs:restriction base="xs:int">
<xs:minInclusive value="0"/>
<xs:maxInclusive value="59"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
flightdata.cxs --- 这是用schema compile对flightdata.xsd编译后的exi文件。试用版本库没有提供该编译器。
7.4.2 编码文件encode.c
编码是把xml文件变成exi文件,带有xsd的编码核心流程:
EFXXMLParseError pError;
EFXFactory * pFactory;
EFXFactoryAlloc(&pFactory);
FILE *in, *out;
in = fopen(fileName, "r"); //fileName = "flightdata.xml"; 指定源文件
out = fopen(encodedFileName, "wb"); //encodedFileName = "flightdata-xcode.xml.exi" 这时输出的exi文件名
EFXFactorySetSchema(pFactory, 0, 0, cxsFileName); //cxsFileName = "flightdata.cxs"; 指定schema文件
EFXTranscoderEncode(pFactory, FileFill, in, fileName, FileFlush, out, &pError)); //编码
fclose(in);
fclose(out);
EFXFactoryFree(pFactory); //释放内存
7.4.3 解码文件decode.c
解码是把exi变成xml文件,核心流程如下:
EFXXMLParseError pError;
EFXFactory * pFactory;
EFXFactoryAlloc(&pFactory);
FILE *in, *out;
in = fopen(encodedFileName, "rb"); //encodedFileName = "flightdata.xml.exi"
out = fopen(decodedFileName, "w"); //decodedFileName = "flightdata-xcode-out.xml" 输出的xml文件名
EFXFactorySetSchema(pFactory, 0, 0, cxsFileName); //cxsFileName = "flightdata.cxs"; 指定schema文件
EFXTranscoderDecode(pFactory, FileFill, in, FileFlush, out); //解码
fclose(in);
fclose(out);
EFXFactoryFree(pFactory); //释放内存
7.4.4 设置option选项文件 settings-file-use.c
该文件做了两部分操作:使用设置文件进行编码,然后手动设置option选项进行解码。
(1)读取设置文件flightdata-settings.xml,包含两个option:
<!-- definitions of the settings can be found in the Java doc for EFXProperty -->
<settings xmlns="http://agiledelta.com/efx/settings/">
<compression>false</compression>
<stringvalues>true</stringvalues>
</settings>
这两个选项将会对编码过程起作用,导致编码出来的Header和Body有所不同。
核心流程:
//加载设置文件,自动配置option
EFXFactoryLoadSettingsFromPath(pFactory, settingsFileName, &pError) //settingsFileName = "flightdata-settings.xml"
(2)手动设置option选项
//手动设置option选项,不使用设置文件
EFXFactorySetProperty(pFactory, EFXPROPERTY_COMPRESSION, 0); // turning off compression makes output bit-aligned
到此为止,作者介绍完了Efficient XML库的基本编解码用法,接下来就要进行各种测试了。
预告:
7.5 Efficient XML库和OpenEXI.jar编解码交叉测试
7.6 EFXReader用法示例
7.7 测试ISO15118-2命令SessionSetupReq、SessionSetupRes
7.8 测试ISO15118-20命令supportedAppProtocolReq、supportedAppProtocolRes