4

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

<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 的经验,但我认为这是处理强类型数据/对象的更好方法?

4

3 回答 3

6

首先,我赞成您的问题,因为它确实提出了一个罕见的情况-根据有多少人传递它来判断,它也很难回答...这也意味着您必须阅读一些内容:) ...

简短的回答是:xsd.exe 创建可用的代码;它可能不是你所期望的,我会解释为什么,但它有效(至少在我的测试中);如果您在交换 XML 时没有问题,那么就按照它的生成方式来处理它。如果没有,Linq 肯定会工作。

因此,主要问题从 XML Schema 的创作方式开始;考虑到它的来源,我很惊讶地看到创作风格中的这种(感知的)模糊性,这最终也是导致 xsd.exe 似乎没有产生预期结果的原因。

请从阅读本文开始,重点关注名为“抽象属性”和“替换组属性”的部分。

通常,替换组的头部应该是一个抽象元素。虽然规范并未强制执行此操作,但我怀疑很多人在他们的工具中做出了这种假设(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 中为您提供 DiagramElement 的 @xsi:type)。

下面的 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。我用这张图来描绘它:

在此处输入图像描述

<?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:type 创作风格)。一个很好的测试可能是查看后一个 XML 是否可以使用 xsd.exe 生成的代码进行反序列化,以及需要进行哪些更改才能使其正常工作。

于 2011-11-24T02:31:13.577 回答
2

我已经找到了解决方案。

据我了解,问题不在于抽象定义,因为 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;
        }
    }

现在它起作用了!

显然,您必须记录并维护修改,以防重新生成类。

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

于 2020-01-24T16:53:32.903 回答
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;

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

于 2011-11-24T11:56:50.640 回答