使用xsd.exe或xsd2code反序列化复杂的xsd模式(包含用于继承的substitutiongroup元素)

时间:2011-11-21 11:02:53

标签: serialization xsd linq-to-xml deserialization xsd2code

我在反序列化/序列化某些xsd模式时遇到问题,特别是在其中使用了替换组元素(substitutiongroup)。我想要做的是从xsd架构生成C#类,然后处理对象,然后将它们序列化为有效的XML格式。 有4个xsd文件,我用xsd2code或xsd.exe反序列化和序列化。这两种工具都会产生类似的不满意结果。它们忽略“substitutiongroup”元素,并且不会正确生成类成员。 当我运行xsd.exe或xsd2code时,为BPMNPlane生成的c#类例如不包含成员BPMNShape(但BPMNDiagram类包含BPMNPlane)。我试图更改生成的C#类(例如添加成员/属性),但生成的XML输出不正确。我想人们可以用linq-to-xml来掌握它,但它们是太多不同的元素,大约70个,带有额外的属性属性。

<xsd:import namespace="http://www.omg.org/spec/DD/20100524/DC" schemaLocation="DC.xsd" />
<xsd:import namespace="http://www.omg.org/spec/DD/20100524/DI" schemaLocation="DI.xsd" />

<xsd:element name="BPMNDiagram" type="bpmndi:BPMNDiagram" />
<xsd:element name="BPMNPlane" type="bpmndi:BPMNPlane" />
<xsd:element name="BPMNLabelStyle" type="bpmndi:BPMNLabelStyle" />
<xsd:element name="BPMNShape" type="bpmndi:BPMNShape" substitutionGroup="di:DiagramElement" />
<xsd:element name="BPMNLabel" type="bpmndi:BPMNLabel" />
<xsd:element name="BPMNEdge" type="bpmndi:BPMNEdge" substitutionGroup="di:DiagramElement" />

<xsd:complexType name="BPMNDiagram">
    <xsd:complexContent>
        <xsd:extension base="di:Diagram">
            <xsd:sequence>
                <xsd:element ref="bpmndi:BPMNPlane" />
                <xsd:element ref="bpmndi:BPMNLabelStyle" maxOccurs="unbounded" minOccurs="0" />
            </xsd:sequence>
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>


<xsd:complexType name="BPMNPlane">
    <xsd:complexContent>
        <xsd:extension base="di:Plane">    
    <xsd:attribute name="bpmnElement" type="xsd:QName" />       
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>

<xsd:complexType name="BPMNEdge">
    <xsd:complexContent>
        <xsd:extension base="di:LabeledEdge">
            <xsd:sequence>
                <xsd:element ref="bpmndi:BPMNLabel" minOccurs="0" />
            </xsd:sequence>
            <xsd:attribute name="bpmnElement" type="xsd:QName" />
            <xsd:attribute name="sourceElement" type="xsd:QName" />
            <xsd:attribute name="targetElement" type="xsd:QName" />
            <xsd:attribute name="messageVisibleKind" type="bpmndi:MessageVisibleKind" />
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>



<xsd:complexType name="BPMNShape">
    <xsd:complexContent>
        <xsd:extension base="di:LabeledShape">
            <xsd:sequence>
                <xsd:element ref="bpmndi:BPMNLabel" minOccurs="0" />
            </xsd:sequence>
            <xsd:attribute name="bpmnElement" type="xsd:QName" />
            <xsd:attribute name="isHorizontal" type="xsd:boolean" />
            <xsd:attribute name="isExpanded" type="xsd:boolean" />
            <xsd:attribute name="isMarkerVisible" type="xsd:boolean" />
            <xsd:attribute name="isMessageVisible" type="xsd:boolean" />
            <xsd:attribute name="participantBandKind" type="bpmndi:ParticipantBandKind" />
            <xsd:attribute name="choreographyActivityShape" type="xsd:QName"/>
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>

<xsd:complexType name="BPMNLabel">
    <xsd:complexContent>
        <xsd:extension base="di:Label">
            <xsd:attribute name="labelStyle" type="xsd:QName" />
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>

<xsd:complexType name="BPMNLabelStyle">
    <xsd:complexContent>
        <xsd:extension base="di:Style">
            <xsd:sequence>
                <xsd:element ref="dc:Font" />
            </xsd:sequence>
        </xsd:extension>
    </xsd:complexContent>
</xsd:complexType>

<xsd:simpleType name="ParticipantBandKind">
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="top_initiating" />
        <xsd:enumeration value="middle_initiating" />
        <xsd:enumeration value="bottom_initiating" />
        <xsd:enumeration value="top_non_initiating" />
        <xsd:enumeration value="middle_non_initiating" />
        <xsd:enumeration value="bottom_non_initiating" />
    </xsd:restriction>
</xsd:simpleType>

<xsd:simpleType name="MessageVisibleKind">
    <xsd:restriction base="xsd:string">
        <xsd:enumeration value="initiating" />
        <xsd:enumeration value="non_initiating" />
    </xsd:restriction>
</xsd:simpleType>

我是新手,没有使用xsd或linq-to-xml的经验,但我认为这是使用强类型数据/对象的更好方法吗?

3 个答案:

答案 0 :(得分:5)

首先,我提出了你的问题,因为它确实带来了一个罕见的情况 - 它也很难被回答,根据有多少人传递它...这也意味着你将不得不做一些阅读:)...

简短的回答是:xsd.exe创建可用的代码;它可能不是你所期望的,我会解释原因,但它有效(至少我的测试);如果你没有交换那个XML的问题,那么就像它生成它的方式一样。如果没有,Linq肯定会工作。

因此,主要问题始于如何创作XML Schema;考虑到it is coming from我在创作风格中看到这种(感知的)歧义感到惊讶,这最终也是xsd.exe似乎没有产生预期结果的原因。

请首先阅读this paper,重点关注名为“抽象属性”和“SubstitutionGroup属性”的部分。

通常,替换组的头部应该是抽象元素。虽然规范没有强制执行,但我怀疑很多人在他们的工具中做出这个假设(xsd.exe是一个)因为否则存在与@xsi:type模糊的风险。

在BPMN模式中,替换组的头部不是抽象的(我看过的那个);更多,用作替换组头部的元素是抽象类型 - 这在xsi:type中响起。总而言之,如果你看看生成的代码,xsd.exe会创建一个完全有效的代码,通过选择使用或不使用xsi:type;它和前者一起去了。

此代码引用xsd.exe生成的代码来创建简单的XML。

BPMNEdge edge = new BPMNEdge();
edge.id = "B2";
// more code here for waypoint
plane.DiagramElement1 = new DiagramElement[] { edge };

DiagramElement1属性基本上会接受从DiagramElement类型派生的任何类型,基本上完全填充契约(并在生成的XML中为您提供给图形元素的@xsi:类型)。

以下XML有效;我无法弄清楚是否使用DiagramElement摘要来解决你的问题......我认为它不会那么简单,但我会把它留给你。

<?xml version="1.0" encoding="utf-16"?>
<BPMNPlane xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="A1" xmlns:q1="urn:tempuri-org:alpha" bpmnElement="q1:test" xmlns="http://www.omg.org/spec/BPMN/20100524/DI">
  <DiagramElement xmlns:q2="http://www.omg.org/spec/BPMN/20100524/DI" xsi:type="q2:BPMNEdge" id="B2" xmlns="http://www.omg.org/spec/DD/20100524/DI">
    <waypoint x="1" y="1" />
    <waypoint x="1" y="1" />
</DiagramElement>
</BPMNPlane> 

下面的(也是有效的)XML是由工具生成的(不是由xsd.exe生成的代码);它显示了一个完全有效的上述XML替代方案,使用替换组的成员,这是你想要的。您所要做的就是找出替代DiagramElement的其他内容。我用这张图来描绘它:

enter image description here

<?xml version="1.0" encoding="utf-16"?>
<!-- Sample XML generated by QTAssistant (http://www.paschidev.com) -->
<BPMNPlane xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" id="A1" xmlns:q1="urn:tempuri-org:alpha" bpmnElement="q1:test" xmlns="http://www.omg.org/spec/BPMN/20100524/DI">
    <BPMNEdge xmlns:dc="http://www.omg.org/spec/DD/20100524/DC" xmlns:di="http://www.omg.org/spec/DD/20100524/DI" p4:any_Attr="anySimpleType" bpmnElement="qname1" sourceElement="qname1" targetElement="qname1" messageVisibleKind="initiating" id="ID1" xmlns:p4="otherNS" xmlns="http://www.omg.org/spec/BPMN/20100524/DI">
        <di:extension/>
        <di:waypoint x="1" y="1"/>
        <di:waypoint x="-1.7976931348623157E+308" y="-1.7976931348623157E+308"/>
        <BPMNLabel p4:any_Attr="anySimpleType" labelStyle="qname1" id="ID2">
            <di:extension/>
            <dc:Bounds x="1" y="1" width="1" height="1"/>
        </BPMNLabel>
    </BPMNEdge>
</BPMNPlane>

我认为这个模式是一个完美的例子,它展示了如何使用一个模式同时拥有它(有或没有xsi:类型创作风格)。一个很好的测试可能是看看后面的XML是否可以使用xsd.exe生成的代码进行反序列化,以及为了使其工作需要进行哪些更改。

答案 1 :(得分:1)

我已经找到了解决方案。

据我了解,问题不在于抽象定义,因为DiagramElement类在规范中定义为抽象,而是BPMNShape类位于DiagramElement类之外的另一个命名空间中。在这种情况下,替换组似乎不起作用。

在BPMN规范中,存在另一种相似的情况,但其中为同一命名空间中的类定义了替换组(例如,参见tUserTask和tFlowElement),并且在这种情况下,它可以工作。

>

我发现问题出在Plane类中的DiagramElement1的定义上,其中xsd创建的类如下:

[System.Xml.Serialization.XmlElementAttribute("DiagramElement")]
    public DiagramElement[] DiagramElement1 {
        get {
            return this.diagramElement1Field;
        }
        set {
            this.diagramElement1Field = value;
        }
    }

我决定不更改原始xsd中的任何内容,而只是通过以下方式更新此类:

    [System.Xml.Serialization.XmlElementAttribute("BPMNEdge", typeof(BPMNEdge), Namespace="http://www.omg.org/spec/BPMN/20100524/DI")]
    [System.Xml.Serialization.XmlElementAttribute("BPMNShape", typeof(BPMNShape), Namespace = "http://www.omg.org/spec/BPMN/20100524/DI")]
    [System.Xml.Serialization.XmlElementAttribute("DiagramElement")]
    public DiagramElement[] DiagramElement1 {
        get {
            return this.diagramElement1Field;
        }
        set {
            this.diagramElement1Field = value;
        }
    }

现在它可以工作了!

很明显,您必须记录并维护所做的修改,以防重新生成类。

我无法找到更好的方法来实现这一目标。如果有人知道该怎么做,请发表评论。

答案 2 :(得分:0)

谢谢你的时间!这是一个很好的解释!我也发现,这样做:

BPMNDiagram bpmnd = new BPMNDiagram();
BPMNPlane bpmnl = bpmnd.BPMNPlane;
 bpmnl.DiagramElement1.Add(new BPMNShape());

有效,它给了我以下XML结构:

                 

但我真正想要的是:

<bpmndi: BPMNDiagram name="bpmndiagramid">
<bpmndi: BPMNPlane>
    <bpmndi:BPMNShape id="11" bpmnElement="functionsname">
        <dc:Bounds x="0" y="0" width="0" height="0"/>
    </bpmndi:BPMNShape>
</bpmndi: BPMNPlane>
</bpmndi: BPMNDiagram >

所以,我可以写:

BPMNDiagram bpmnd = new BPMNDiagram();
BPMNPlane bpmnl = bpmnd.BPMNPlane;

给了我:

< BPMNDiagram name="bpmndiagramid">
< BPMNPlane>...
</ BPMNPlane>
</ BPMNDiagram >

但不是直接这个:

BPMNDiagram bpmnd = new BPMNDiagram();
BPMNPlane bpmnl = bpmnd.BPMNPlane;
BPMNShape myShape = bpmnl.BPMNShape;

我认为,使用LINQ 2 XML比生成c#类并使用它们更快,但我现在看到我必须深入研究XML Schema / Elements等。