# OfficeXml [迁移指南](./%E8%BF%81%E7%A7%BB%E6%8C%87%E5%8D%97.md) ## 概述 将 docx、pptx、xlsx 等文件中的 xml 转为 tsl 对象 ```xml 最小申购、赎回单位 (份) ``` 上述是一个 docx 中的段落的 xml,序列化为 tsl 过程如下 ```go uses DocxML; // 上述xml属于DocxML p := new P(); // 创建一个P对象(段落w:p) p.Init(node); // 假设node节点是上面的xml指向的Node对象 p.Deserialize(); // 将node对象的xml序列化到tsl对象 // 序列化完毕后,可直接对应取值 echo p.PPr.Jc.Val; // 输出:left // 如果需要修改内容,通过Serialize()回写到node p.PPr.Jc.Val := "center"; // 回写方法1,适合修改单个对象 p.PPr.Jc.Serialize(); // 回写方法2,大量修改,不同的对象 p.Serialize(); // 或p.PPr.Serialize() // 在获取存在多个节点的对象时,比如上述的w:r对象是复数的,则需要通过Rs()获取 // 直接调用Rs()会获取所有的R对象,加上索引会获取第N+1个 echo p.Rs(1).T.Text; // 输出:(份) ``` ## 基类 `OpenXmlAttribute`是节点的属性类 `OpenXmlElement`是节点基类 `OpenXmlSimpleType`是简单类型的元素节点类,继承于 `OpenXmlElement`,如 `` `OpenXmlTextElement`是包含文本内容的元素节点类,继承于 `OpenXmlElement`,如 `文本` `OpenXmlCompositeElement`是复杂节点的元素节点类,继承于 `OpenXmlElement`,会包含一系列的属性或其他元素节点类 ## 基本功能 ### 获取属性/子节点 获取同名但命名空间不同的属性/子节点内容时,遵守以下规范 1. 默认情况下,获取的命名空间前缀与父节点一致 2. 当父节点有前缀,但是属性/子节点没有前缀时候,通过 `("")`空字符串参数获取 3. 存在同名属性/子节点情况下,默认获取的一定是和父节点前缀相同的内容 ```xml cos ``` ```go r.Val; // 默认获取前缀为m,即m:val的属性 r.Val("m"); // 指定前缀m r.Val(""); // 无前缀的属性,即val r.Val("w"); // 获取前缀为w,即w:val的属性 r.RPr; // 默认获取前缀为m,即m:rPr的子节点 r.RPr("m"); // 指定前缀为m r.RPr("a"); // 获取前缀为a,即a:rPr的子节点 ``` ### 删除属性/节点 ```go 移除方式一:通过RemoveAttribute或RemoveChild // 移除r的属性w:val // 因为r.Val("w")是直接获取了属性值,所以不能作为参数传递给RemoveAttribute r.RemoveAttribute(r.Attribute("w:val")); // 移除r的子节点rPr // 因为r.RPr就是一个对象,所以可直接使用 r.RemoveChild(r.RPr); 移除方式二:通过赋值nil // 这种方式不需要区分属性和子节点,也不需要知道属性的全称 r.Val("w") := nil; r.RPr := nil; 最后需要回写才会生效 r.Serialize(); ``` ### 回落功能(Fallback) 项目的 `fallback`功能是指获取某个属性或节点不存在时候,会检查是否设置了 `fallback`,如果设置了会通过 `fallback`获取对应的属性或节点 ```xml // pPr1 // pPr2 ``` ```go // 假设已经获取到了对象ppr1和ppr2 ppr1.SetFallback(ppr2); // 将ppr1的fallback设置为ppr2 ppr1.Jc.Val; // 得到"left" ppr1.WordWrap.Val; // ppr1不存在wordWrap,但是ppr2存在wordWrap,所以回落到ppr2的wordWrap获取到"1" ``` ### Copy `copy`方法支持将其他的类属性和子节点的属性复制过来,注意:复数的类无法进行复制,因为不确定怎么进行复制 ```xml // pPr1 // pPr2 ``` ```go // 假设要将ppr2的属性复制到ppr1 ppr1.Copy(ppr2); ``` ```xml // 复制完毕后ppr1 xml如下 // 因为ppr2没有w:spacing,所以保留ppr1的w:spacing ``` ```xml // 这样是不能复制的,w:r是复数 test 获取r并不是p.r进行获取,而是p.Rs(); ``` ### Clone `Clone`方法是克隆出一个一样的对象出来,但是`Parent`是`nil` ```xml test ``` ```go new_p := p.Clone(); // 与p的xml一致 echo new_p.Parent; // nil document.InsertAfter(new_p, p); // 插入一样的段落在p之后,此时会自动设置new_p.Parent := document ``` ## Unit 单元 - `DocxML`包含 `docx`文件独有的 xml 节点对象,一般 xml 的命名空间是 `w`,如 `w:p` - `PptxML`包含 `pptx`文件独有的 xml 节点对象,一般 xml 的命名空间是 `p`,如 `p:spPr` - `XlsxML`包含 `xlsx`文件独有的 xml 节点对象 - `DrawingML`包含 `docx,pptx,xlsx`文件图形的 xml 节点对象,一般 xml 的命名空间是 `a`,如 `a:xfrm` - `SharedML`包含 `docx,pptx,xlsx`文件共有的 xml 节点对象 - `VML` [参考链接 1](http://webapp.docx4java.org/OnlineDemo/ecma376/) [参考链接 2](http://officeopenxml.com/index.php) ## 部件 ### Components 一共有三个部件,分别是: 1. `DocxComponents.tsf`:docx 文件的各部分 xml 内容 2. `XlsxComponents`:xlsx 文件的各部分 xml 内容 3. `PptxComponents`:pptx 文件的各部分 xml 内容 以 `DocxComponents.tsf`为例,使用这个类,可以获取到对应的 docx 文件的 xml 对象 ```go component := new DocxComponents(); // 创建对象 component.Open("", "xxx.docx"); // 打开文件 document := component.Document; // 获取document.xml,生成Document对象 document.Deserialize(); // 将xml对象的数据反序列化到tsl对象中 document.Body.Elements(); // 可以获取document的body下的所有对象列表 // 反序列化后,可进行读写 styles := component.Styles; // 获取styles.xml,生成Styles对象 ``` document.xml 内容如下 ```xml 分栏前 ``` ### 单位装饰器 UnitDecorator 每个对象都有一个单位装饰器,能统一转成磅(point)单位(如果有配置属性转换),还能保留原来的接口 每个 `ML`都有装饰器 `tsf`统一命名是 `Unit的名称+UnitDecorator`,如 `docx`(`docx`大部分 `xml`隶属于 `DocxML`)的 `SectPr`对象的装饰器是 `SectPrUnitDecorator` 如:有下面一段 xml,其中的 `pgSz.w = "11906", pgSz.h = "16838"`都需要转换成 point ```xml ``` ```go uses DocxMLUnitDecorator; component := new DocxComponents(); // 创建对象 component.Open("", "xxx.docx"); // 打开文件 document := component.Document; // 获取document.xml,生成Document对象 document.Deserialize(); // 将xml对象的数据反序列化到tsl对象中 sect_pr := document.Body.SectPr; // 获取SectPr对象 sect_pr_unit_decorator := new SectPrUnitDecorator(sect_pr); // 装饰器构造需要原本的对象 echo "w = ", sect_pr.PgSz.W; // 输出的是字符串,原本的单位是twips echo "\n"; echo "w = ", sect_pr_unit_decorator.PgSz.W; // 此时输出的是数字类型,单位是point ``` ### 适配器 Adapter 适配器是通过 key 获取对应的对象,比如样式可以通过样式 ID 获取对应的样式对象 只有部分对象才有适配器(具体可见 `autounit/xxxMLAdapter`),比如 `DocxML.Styles`的适配器是 `StylesAdapter` styles.xml 部分如下 ```xml ``` ```go uses DocxMLAdapters; component := new DocxComponents(); // 创建对象 component.Open("", "xxx.docx"); // 打开文件 document := component.Document; // 获取document.xml,生成Document对象 document.Deserialize(); // 将xml对象的数据反序列化到tsl对象中 styles := document.Styles; // 现在需要通过styleId获取Style对象 styles_adapter := new DocxMLAdapters.Styles(styles); // 建议指定unit,否则容易冲突 // 通过StyleId获取Style对象 style := styles_adapter.GetStyleByStyleId("a6"); echo style.Name; // 输出的是"页脚 字符" ```