我在反序列化/序列化某些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的经验,但我认为这是使用强类型数据/对象的更好方法吗?
答案 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的其他内容。我用这张图来描绘它:
<?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等。