在不修改 C# XSD 类的情况下向 XML 序列化添加前缀和命名空间
Posted
技术标签:
【中文标题】在不修改 C# XSD 类的情况下向 XML 序列化添加前缀和命名空间【英文标题】:Add prefixes and namespaces to XML serialisation WITHOUT amending C# XSD class 【发布时间】:2018-03-23 14:49:17 【问题描述】:我们使用 XSD 文件,published by Agresso 生成我们的 XML 文件。
我已经生成了XSD类,使用xsd.exe,可以根据客户的要求成功生成XML文件。
命名空间区域看起来像
<ABWInvoice xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:agrlib="http://services.agresso.com/schema/ABWSchemaLib/2011/11/14" xmlns="http://services.agresso.com/schema/ABWInvoice/2011/11/14">
我通过以下代码实现:
public static string XmlNamespace<T>(T entity) where T: class
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
settings.OmitXmlDeclaration = true; //removing the encoding, e.g. instead of <?xml version="1.0" encoding="utf-16"?> should be <?xml version="1.0"?>
using (StringWriter sw = new StringWriter())
using (XmlWriter writer = XmlWriter.Create(sw, settings))
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("agrlib", "http://services.agresso.com/schema/ABWSchemaLib/2011/11/14");
ns.Add("xsi","http://www.w3.org/2001/XMLSchema-instance");
XmlSerializer xser = new XmlSerializer(typeof(T));
xser.Serialize(writer, entity, ns);
return sw.ToString();
我们现在有一个客户,需要添加额外的前缀:“xsi:schemaLocation”,所以它将是
<ABWInvoice xsi:schemaLocation="http://services.agresso.com/schema/ABWInvoice/2011/11/14" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:agrlib="http://services.agresso.com/schema/ABWSchemaLib/2011/11/14" xmlns="http://services.agresso.com/schema/ABWInvoice/2011/11/14">
我在网上找到了很多示例,但它们都添加了 xmlns:schemaLocation 而不是 xsi:schemaLocation。我也不能修改 xsd 类,因为它会影响其他客户。
由于我是 C# 新手,能否请教一下如何处理这样的请求?
【问题讨论】:
【参考方案1】:AFAIK,在代码中做到这一点的唯一方法是添加如下内容:
public class YourRootType
[XmlAttribute("schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Bar
get => "http://services.agresso.com/schema/ABWInvoice/2011/11/14";
set
但这会影响所有实例。另一种方法可能是通过 XSLT 之类的东西添加值,但这很难看。如果您愿意将其留在代码中,您可以使用条件序列化,即仅在设置时输出;例如:
[XmlAttribute("schemaLocation", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
public string Bar get; set;
// this pattern is recognized by the serializer itself
public bool ShouldSerializeBar() => !string.IsNullOrWhiteSpace(Bar);
并在运行时为这些客户端将Bar
设置为"http://services.agresso.com/schema/ABWInvoice/2011/11/14"
。
另一种选择是使用[XmlAnyAttribute]
:
[XmlAnyAttribute]
XmlAttribute[] AdditionalAttributes get; set;
并在运行时附加任意附加属性。不过,这有点复杂,因为通过 C# 获取 XmlAttribute
实例有点尴尬——你需要创建一个 DOM 等。
我忘了说;就使其成为xsi:schemaLocation
而言:这就是XmlSerializerNamespaces
的用武之地:
XmlSerializerNamespaces ns = new XmlSerializerNamespaces();
ns.Add("", "");
ns.Add("xsi", "http://www.w3.org/2001/XMLSchema-instance");
ns.Add("agrlib", "http://services.agresso.com/schema/ABWSchemaLib/2011/11/14");
// etc
并将ns
作为XmlSerializer.Serialize
的最后一个参数传入。
【讨论】:
对于 C# 新手来说太复杂了,但现在就试试吧!我唯一不明白的是,您的示例中的 Bar 将如何与 schemaLocation 关联。你能解释一下吗?非常感谢。 @KDWolf 您的第 3 方要求您做的事情不是添加名称空间或前缀或类似的东西;他们要求您在命名空间"http://www.w3.org/2001/XMLSchema-instance"
中添加一个本地名称为"schemaLocation"
的属性,其值为"http://services.agresso.com/schema/ABWInvoice/2011/11/14"
- 最好是也命名空间别名 "xsi"
对应于 "http://www.w3.org/2001/XMLSchema-instance"
命名空间,其效果是您的属性也可以通过 "xsi:schemaLocation"
引用。 [XmlAttribute]
上的 Bar
(1/2)
(2/2) 定义名称和命名空间; XmlSerializerNamespaces
定义了一组命名空间别名,值来自显示的代码。这就是这一切联系在一起的方式。这能回答问题吗?还是还不清楚?
是的,确实如此!塔。抱歉,我没有足够的声誉将您的答案标记为有用(至少需要 15 个)。
谢谢你,@Marc。它真的成功了。我可以再问你两个问题吗:1)当我在你的例子中添加ns.Add("", "");
时,所有元素都有一个 q1 前缀,例如<q1:ABWInvoice xmlns:agrlib="http://services.agresso.com/schema/ABWSchemaLib/2011/11/14" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://services.agresso.com/schema/ABWInvoice/2011/11/14" xmlns:q1="http://services.agresso.com/schema/ABWInvoice/2011/11/14"> <q1:Invoice>
我必须添加其他内容吗? (1/2)以上是关于在不修改 C# XSD 类的情况下向 XML 序列化添加前缀和命名空间的主要内容,如果未能解决你的问题,请参考以下文章
在不破坏现有基类的情况下向具有许多派生类的抽象基类添加新方法
在不修改 config.ini 的情况下向已安装的 eclipse rcp 软件添加插件