六、choice、sequence、maxocc、minocc的映射
简单的可以用List实现,复杂的必须自己写程序控制。
例子:CT_Body中EG_ContentBlockContent的映射方式,EG_ContentBlockContent包含了choice和maxocc。
xsd:
1、 精确映射,即实现一一映射。
下面的类实现Unbounded
不足:调用者必须知道存在EG_ContentBlockContent组,而我希望所有组都对调用者透明。public class U_EG_ContentBlockContent Content;
{
public IList<EG_ContentBlockContent> Content;
}
{
#region customXml
private CT_CustomXmlBlock customXmlField;
/// <summary>
/// Block-Level Custom XML Element
/// - Required
/// </summary>
public CT_CustomXmlBlock customXml
{
get { return customXmlField; }
set { customXmlField = value; }
}
#endregion // customXml
#region sdt
private CT_SdtBlock sdtField;
/// <summary>
/// Block-Level Structured Document Tag
/// - Required
/// </summary>
public CT_SdtBlock sdt
{
get { return sdtField; }
set { sdtField = value; }
}
public CT_P[] Paragraphs;
public CT_Tbl[] Tbl;
#endregion // sdt
}
这种方式是最准确的映射,代价就是很难实现choice复杂,并且对调用者来说也不方便(这点可以用一个接口绕过,但是第一点确实很烦人)。
1、 不精确的xsd映射,xsd和类不存在一一对应关系。
仔细分析CT_Body的xsd定义,根据定义xsd考虑所有可能产生的xml形式。让后用类包含所有形式。
EG_ContentBlockContent包含四个元素和一个组,组可以用继承,四个元素中的两个也是unbounded,另外两个的maxocc=1,由于EG_ContentBlockContent是作为unbounded引用的,所以四个元素实际上都是unbounded,他们在xml中可以以任意顺序出现n次。
至此可以产生下面的映射方式:
{
#region customXml
/// <summary>
/// Block-Level Custom XML Element
/// - Required
/// </summary>
public CT_CustomXmlBlock[] customXml
{
get { // 类似p}
}
#endregion // customXml
/// <summary>
/// Block-Level Structured Document Tag
/// - Required
/// </summary>
public CT_SdtBlock[] sdt
{
get { // 类似p }
}
#endregion // sdt
#region Content
private System.Collections.Generic.List < IContent > content;
/// <summary>
/// The content of the body (to keep the order in the Paragraphs and Tables)
/// </summary>
public System.Collections.Generic.List < IContent > Content
{
get
{
if (content == null )
{
content = new System.Collections.Generic.List < IContent > ();
}
return content;
}
}
#endregion
#region p
/// <summary>
/// Paragraphs
/// - Not required
/// To Add a Table use the Content Property
/// </summary>
public CT_P[] Paragraphs
{
get
{
if (content == null )
{
return new CT_P[ 0 ];
}
else
{
System.Collections.Generic.List < CT_P > list = new System.Collections.Generic.List < CT_P > ();
foreach (IContent elts in content)
{
if (elts is CT_P)
{
list.Add((CT_P)elts);
}
}
return list.ToArray();
}
}
}
#endregion // p
#region tbl
/// <summary>
/// Table
/// - Not required
/// To Add a Table use the Content Property
/// </summary>
public CT_Tbl[] tbl
{
get
{
if (content == null )
{
return new CT_Tbl[ 0 ];
}
else
{
System.Collections.Generic.List < CT_Tbl > list = new System.Collections.Generic.List < CT_Tbl > ();
foreach (IContent elts in content)
{
if (elts is CT_Tbl)
{
list.Add((CT_Tbl)elts);
}
}
return list.ToArray();
}
}
}
}
这要求所有的组成员必须实现某个接口,在此用了IContent。这样定义Group,从本质上看已经不是xsd的group了。在这个元素中这样处理简单,但是在别的元素中就不一定,它增加了问题的不确定性,或许后面都可以这样映射,也可能回出现大问题,不得不采用第一种方式,这样就又会把结构打乱,问题会变得更复杂,这也是为啥要尽可能的把类和xsd一一对应,如此一来,结构上有ms设计的xsd做参考,一切的变化都不会太离谱,ms改变xsd后,类库修改也小些。
3、最后看看WarstarDev.Office2k7的映射方式(错误的):
{
#region customXml
private CT_CustomXmlBlock customXmlField;
/// <summary>
/// Block-Level Custom XML Element
/// - Required
/// </summary>
public CT_CustomXmlBlock customXml
{
get { return customXmlField; }
set { customXmlField = value; }
}
#endregion // customXml
#region sdt
private CT_SdtBlock sdtField;
/// <summary>
/// Block-Level Structured Document Tag
/// - Required
/// </summary>
public CT_SdtBlock sdt
{
get { return sdtField; }
set { sdtField = value; }
}
#endregion // sdt
#region Content
private System.Collections.Generic.List < EG_RunLevelElts > content;
/// <summary>
/// The content of the body (to keep the order in the Paragraphs and Tables)
/// </summary>
public System.Collections.Generic.List < EG_RunLevelElts > Content
{
get
{
if (content == null )
{
content = new System.Collections.Generic.List < EG_RunLevelElts > ();
}
return content;
}
}
#endregion
#region p
/// <summary>
/// Paragraphs
/// - Not required
/// To Add a Table use the Content Property
/// </summary>
public CT_P[] Paragraphs
{
get
{
if (content == null )
{
return new CT_P[ 0 ];
}
else
{
System.Collections.Generic.List < CT_P > list = new System.Collections.Generic.List < CT_P > ();
foreach (EG_RunLevelElts elts in content)
{
if (elts is CT_P)
{
list.Add((CT_P) elts);
}
}
return list.ToArray();
}
}
}
#endregion // p
#region tbl
/// <summary>
/// Table
/// - Not required
/// To Add a Table use the Content Property
/// </summary>
public CT_Tbl[] tbl
{
get
{
if (content == null )
{
return new CT_Tbl[ 0 ];
}
else
{
System.Collections.Generic.List < CT_Tbl > list = new System.Collections.Generic.List < CT_Tbl > ();
foreach (EG_RunLevelElts elts in content)
{
if (elts is CT_Tbl)
{
list.Add((CT_Tbl) elts);
}
}
return list.ToArray();
}
}
}
#endregion // tbl
}
错误的地方是,它把我上面说的两种方式和在了一起(更像第二种),造成CT_CustomXmlBlock、CT_SdtBlock没有实现unbounded,是个四不像的映射方式。其次他借用EG_RunLevelElts来管理Ilist元素,有点太取巧了(感觉不会是MS为程序映射xsd而故意设计的,但是整个EG_ContentBlockContent的元素都实现了EG_RunLevelElts,实在是巧合!!),在此可能简单写,但是对程序结构并没有好处。
两种方式各有优缺点,半斤八两,真不知道用哪种方式好些,第一种方式choice很难处理,我想到的只有在运行时做检测一种办法,希望有人能提出更好的解决方案。
第二种把choice绕过去了,但是他并不是xsd到类的一一映射,风险很大,很可能是灾难性的。
现在我采用的是另一种方式,但现在已经走到死路了,逻辑变的越来越复杂,必须改成这两种之一。目前比较倾向于第二种,第一种方式对调用者来说,把检测推到运行时几乎是不能忍受的,但又担心第二种会出现我现在碰到的情况,最终复杂到没有办法处理。
七、sequence和choice类似,就不说了。其他问题就是由于.net不支持多继承导致的,解决方式只能是在映射时做些调整,又是一个必须要仔细权衡的问题,弄不好也会把结构弄的特别乱。
总,想写一个最好的WordMl库,目前还赶不上WarstarDev.Office2k7,但是它用反射、XmlWriter解析的方式已经决定它的灵活性和功能都是有限的,再加上它在映射xsd方面的错误、它的结构也不太好,总会超过它的。
WordMl实在是个超级复杂的东西,尤其是采用Dom解析方式时,我做到现在结构已经大调两次了,但是还必须再做一次大的调整才可能把结构稳定下来,到今天才算把wordml的结构弄清楚,入门了,希望这次调整是最后一次,两个月下来快被它弄死了。解析WordprocessingML(一)查找和替换
http://www.cnblogs.com/bluewater/admin/EditPosts.aspx?postid=627710
解析WordprocessingML(二)通过数据集自动生成表格
http://www.cnblogs.com/bluewater/archive/2007/03/02/662040.html
解析WordprocessingML(三)解析WordMl的基本方法和途径(a)
http://www.cnblogs.com/bluewater/archive/2007/03/02/661824.html
解析WordprocessingML(三)解析WordMl的基本方法和途径(b)
http://www.cnblogs.com/bluewater/archive/2007/03/02/661885.html
解析WordprocessingML(四)转换到Html格式
Xsd入门
http://www.cnblogs.com/bluewater/archive/2007/03/02/661927.html