向 MessageContract WCF 响应对象 (MessageBodyMember) 添加多个命名空间
Adding multiple namespaces to MessageContract WCF response object (MessageBodyMember)
我们有一个包含以下合同的 WCF 设置:
[ServiceContract(
Namespace = Constants.Namespaces.HL7Namespace,
Name = Constants.Roles.ContentRequiredDocumentManagementSystem)]
// XmlSerializerFormat is needed to expose the HL7 schema fields without the "Field" suffix on each one, eg: idField
[XmlSerializerFormat]
public interface ICDARequest
{
[OperationContract(
// wsdl request action
Action = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000029UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion,
// wsdl operation name
Name = Constants.Interactions.RCMR_IN000029UV01,
// wsdl response action
ReplyAction = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000030UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion)]
SearchMessagesResponse SearchMessages(SearchMessagesRequest RCMR_IN000029UV01);
[MessageContract(
IsWrapped = false]
public class SearchMessagesResponse
{
[MessageBodyMember(
Name = State.Constants.Interactions.RCMR_IN000030UV01,
Namespace = State.Constants.Namespaces.HL7Namespace)]
public RCMR_IN000030UV01 data;
}
}
- 这些基于使用
xsd.exe
基于 HL7v3 架构生成的 classes。
- 然后我们更改了架构以使用自定义命名空间添加自定义元素以区分它并重新生成 classes。
- 这很好用。
添加了:
[System.Xml.Serialization.XmlTypeAttribute(TypeName = "BCCDX.DistributionStatus", Namespace = "urn:bccdx.ca")]
public partial class BCCDXDistributionStatus
{
[System.Xml.Serialization.XmlElementAttribute("receivedTime", Namespace = "urn:bccdx.ca", IsNullable = false)]
public TS receivedTime{...}
}
这是我们想要的。
然后在 WCF 服务中我们可以使用新的 class 和成员:
var distStatus = new BCCDXDistributionStatus();
distStatus.receivedTime = CreateTS(locStat.MessageDownloadDate);
然后将其序列化并通过网络发送出去,如下所示:
<distributionStatus xmlns="urn:bccdx.ca">
<receivedTime value="201702150956-0800"/>
</distributionStatus>
这几乎是正确的。问题在于 XML 文档没有引用 "urn:bccdx.ca"
命名空间。我原以为它会在序列化时自动添加到文档根元素,但我错了。这是最终的样子:
<RCMR_IN000030UV01 ITSVersion="XML_1.0" xmlns="urn:hl7-org:v3">
...
</RCMR_IN000030UV01>
当真正想要的是:
<RCMR_IN000030UV01 ITSVersion="XML_1.0" xmlns="urn:hl7-org:v3" xmlns:x="urn:bccdx.ca">
...
</RCMR_IN000030UV01>
请注意带有前缀
的urn:bccdx.ca
我想知道如果我们可以添加多个名称空间,并通过合同为生成的序列化消息 XML 添加前缀?我在网上看到了覆盖默认序列化程序的提示,但我宁愿不这样做。这肯定是以前考虑过和处理过的吧?
首先,我将假设,在您的服务合同中的某处,您通过使用 [XmlSerializerFormat]
指定使用 XmlSerializer
,例如:
[ServiceContract()]
[XmlSerializerFormat]
public interface IService1
{
[OperationContract(
// wsdl request action
Action = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000029UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion,
// wsdl operation name
Name = Constants.Interactions.RCMR_IN000029UV01,
// wsdl response action
ReplyAction = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000030UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion)]
SearchMessagesResponse SearchMessages(/* SearchMessagesRequest RCMR_IN000029UV01*/);
}
虽然您的问题中没有提到这一点,但如果您没有这样做,那么您类型中的 [System.Xml.Serialization.XmlElementAttribute(...)]
属性声明将没有任何效果,因为它们会被 DataContractSerializer
忽略。
其次,我假设您的 RCMR_IN000030UV01
类型当前看起来像这样:
[XmlRoot(ElementName = "RCMR_IN000030UV01", Namespace = "urn:hl7-org:v3")]
public partial class RCMR_IN000030UV01
{
// The initially auto-generated code
[XmlAttribute(AttributeName = "ITSVersion")]
public string ITSVersion { get; set; }
}
public partial class RCMR_IN000030UV01
{
// The added property
[System.Xml.Serialization.XmlElementAttribute("distributionStatus", Namespace = "urn:bccdx.ca", IsNullable = false)]
public BCCDXDistributionStatus distStatus { get; set; }
}
[System.Xml.Serialization.XmlTypeAttribute(TypeName = "BCCDX.DistributionStatus", Namespace = "urn:bccdx.ca")]
public partial class BCCDXDistributionStatus
{
[System.Xml.Serialization.XmlElementAttribute("receivedTime", Namespace = "urn:bccdx.ca", IsNullable = false)]
public TS receivedTime { get; set; }
}
public class TS
{
[XmlAttribute("value")]
public DateTime Value { get; set; }
}
目前您的服务 returning XML 看起来像这样:
<RCMR_IN000030UV01 ITSVersion="1.0"
xmlns="urn:hl7-org:v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<distributionStatus
xmlns="urn:bccdx.ca">
<receivedTime value="2017-02-23T00:00:00-05:00"/>
</distributionStatus>
</RCMR_IN000030UV01>
但是,你想要这个:
<RCMR_IN000030UV01 ITSVersion="1.0"
xmlns="urn:hl7-org:v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
<!---This should be added ---->
xmlns:x="urn:bccdx.ca">
<!---And distributionStatus should be prefixed with x: ---->
<x:distributionStatus>
<x:receivedTime value="2017-02-23T00:00:00-05:00"/>
</x:distributionStatus>
</RCMR_IN000030UV01>
首先我会注意到这两个 XML 文件在语义上是相同的。在第一种情况下,命名空间 "urn:bccdx.ca"
被声明为实际需要它的最低元素上的默认命名空间。在第二个中,它是在文件开头用前缀定义的。无论哪种方式,元素 <distributionStatus>
及其子元素最终都会出现在正确的命名空间中。
因此,您可以简单地接受 XML 原样正确。
如果出于某种原因,您必须让命名空间出现在 XML 的开头并带有 x:
前缀,您可以添加一个[XmlNamespaceDeclarations]
属性 到您的 RCMR_IN000030UV01
以强制在更高级别声明您的命名空间:
public partial class RCMR_IN000030UV01
{
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces xmlsn
{
get
{
var ns = new XmlSerializerNamespaces();
ns.Add("x", "urn:bccdx.ca");
return ns;
}
set
{
// Do nothing - fake property.
}
}
}
如 docs 中所述,此属性
Specifies that the target property, parameter, return value, or class member contains prefixes associated with namespaces that are used within an XML document.
现在您的服务应该 return XML 具有根元素上所需的命名空间。
我们有一个包含以下合同的 WCF 设置:
[ServiceContract(
Namespace = Constants.Namespaces.HL7Namespace,
Name = Constants.Roles.ContentRequiredDocumentManagementSystem)]
// XmlSerializerFormat is needed to expose the HL7 schema fields without the "Field" suffix on each one, eg: idField
[XmlSerializerFormat]
public interface ICDARequest
{
[OperationContract(
// wsdl request action
Action = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000029UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion,
// wsdl operation name
Name = Constants.Interactions.RCMR_IN000029UV01,
// wsdl response action
ReplyAction = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000030UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion)]
SearchMessagesResponse SearchMessages(SearchMessagesRequest RCMR_IN000029UV01);
[MessageContract(
IsWrapped = false]
public class SearchMessagesResponse
{
[MessageBodyMember(
Name = State.Constants.Interactions.RCMR_IN000030UV01,
Namespace = State.Constants.Namespaces.HL7Namespace)]
public RCMR_IN000030UV01 data;
}
}
- 这些基于使用
xsd.exe
基于 HL7v3 架构生成的 classes。 - 然后我们更改了架构以使用自定义命名空间添加自定义元素以区分它并重新生成 classes。
- 这很好用。
添加了:
[System.Xml.Serialization.XmlTypeAttribute(TypeName = "BCCDX.DistributionStatus", Namespace = "urn:bccdx.ca")]
public partial class BCCDXDistributionStatus
{
[System.Xml.Serialization.XmlElementAttribute("receivedTime", Namespace = "urn:bccdx.ca", IsNullable = false)]
public TS receivedTime{...}
}
这是我们想要的。
然后在 WCF 服务中我们可以使用新的 class 和成员:
var distStatus = new BCCDXDistributionStatus();
distStatus.receivedTime = CreateTS(locStat.MessageDownloadDate);
然后将其序列化并通过网络发送出去,如下所示:
<distributionStatus xmlns="urn:bccdx.ca">
<receivedTime value="201702150956-0800"/>
</distributionStatus>
这几乎是正确的。问题在于 XML 文档没有引用 "urn:bccdx.ca"
命名空间。我原以为它会在序列化时自动添加到文档根元素,但我错了。这是最终的样子:
<RCMR_IN000030UV01 ITSVersion="XML_1.0" xmlns="urn:hl7-org:v3">
...
</RCMR_IN000030UV01>
当真正想要的是:
<RCMR_IN000030UV01 ITSVersion="XML_1.0" xmlns="urn:hl7-org:v3" xmlns:x="urn:bccdx.ca">
...
</RCMR_IN000030UV01>
请注意带有前缀
的urn:bccdx.ca我想知道如果我们可以添加多个名称空间,并通过合同为生成的序列化消息 XML 添加前缀?我在网上看到了覆盖默认序列化程序的提示,但我宁愿不这样做。这肯定是以前考虑过和处理过的吧?
首先,我将假设,在您的服务合同中的某处,您通过使用 [XmlSerializerFormat]
指定使用 XmlSerializer
,例如:
[ServiceContract()]
[XmlSerializerFormat]
public interface IService1
{
[OperationContract(
// wsdl request action
Action = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000029UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion,
// wsdl operation name
Name = Constants.Interactions.RCMR_IN000029UV01,
// wsdl response action
ReplyAction = Constants.Namespaces.HL7Namespace + ":" + Constants.Interactions.RCMR_IN000030UV01 + "." + Constants.VersionType.NormativeCode + Constants.Version.InteractionVersion)]
SearchMessagesResponse SearchMessages(/* SearchMessagesRequest RCMR_IN000029UV01*/);
}
虽然您的问题中没有提到这一点,但如果您没有这样做,那么您类型中的 [System.Xml.Serialization.XmlElementAttribute(...)]
属性声明将没有任何效果,因为它们会被 DataContractSerializer
忽略。
其次,我假设您的 RCMR_IN000030UV01
类型当前看起来像这样:
[XmlRoot(ElementName = "RCMR_IN000030UV01", Namespace = "urn:hl7-org:v3")]
public partial class RCMR_IN000030UV01
{
// The initially auto-generated code
[XmlAttribute(AttributeName = "ITSVersion")]
public string ITSVersion { get; set; }
}
public partial class RCMR_IN000030UV01
{
// The added property
[System.Xml.Serialization.XmlElementAttribute("distributionStatus", Namespace = "urn:bccdx.ca", IsNullable = false)]
public BCCDXDistributionStatus distStatus { get; set; }
}
[System.Xml.Serialization.XmlTypeAttribute(TypeName = "BCCDX.DistributionStatus", Namespace = "urn:bccdx.ca")]
public partial class BCCDXDistributionStatus
{
[System.Xml.Serialization.XmlElementAttribute("receivedTime", Namespace = "urn:bccdx.ca", IsNullable = false)]
public TS receivedTime { get; set; }
}
public class TS
{
[XmlAttribute("value")]
public DateTime Value { get; set; }
}
目前您的服务 returning XML 看起来像这样:
<RCMR_IN000030UV01 ITSVersion="1.0"
xmlns="urn:hl7-org:v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<distributionStatus
xmlns="urn:bccdx.ca">
<receivedTime value="2017-02-23T00:00:00-05:00"/>
</distributionStatus>
</RCMR_IN000030UV01>
但是,你想要这个:
<RCMR_IN000030UV01 ITSVersion="1.0"
xmlns="urn:hl7-org:v3"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
<!---This should be added ---->
xmlns:x="urn:bccdx.ca">
<!---And distributionStatus should be prefixed with x: ---->
<x:distributionStatus>
<x:receivedTime value="2017-02-23T00:00:00-05:00"/>
</x:distributionStatus>
</RCMR_IN000030UV01>
首先我会注意到这两个 XML 文件在语义上是相同的。在第一种情况下,命名空间 "urn:bccdx.ca"
被声明为实际需要它的最低元素上的默认命名空间。在第二个中,它是在文件开头用前缀定义的。无论哪种方式,元素 <distributionStatus>
及其子元素最终都会出现在正确的命名空间中。
因此,您可以简单地接受 XML 原样正确。
如果出于某种原因,您必须让命名空间出现在 XML 的开头并带有 x:
前缀,您可以添加一个[XmlNamespaceDeclarations]
属性 到您的 RCMR_IN000030UV01
以强制在更高级别声明您的命名空间:
public partial class RCMR_IN000030UV01
{
[XmlNamespaceDeclarations]
public XmlSerializerNamespaces xmlsn
{
get
{
var ns = new XmlSerializerNamespaces();
ns.Add("x", "urn:bccdx.ca");
return ns;
}
set
{
// Do nothing - fake property.
}
}
}
如 docs 中所述,此属性
Specifies that the target property, parameter, return value, or class member contains prefixes associated with namespaces that are used within an XML document.
现在您的服务应该 return XML 具有根元素上所需的命名空间。