在不修改 C# XSD class 的情况下向 XML 序列化添加前缀和命名空间

Add prefixes and namespaces to XML serialisation WITHOUT amending C# XSD class

我们使用 XSD 文件,published by Agresso 生成我们的 XML 文件。

我已经使用 xsd.exe 生成 XSD class,并且可以根据客户的要求成功生成 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。另外我不能修改xsdclass,因为这会影响其他客户。

由于我是 C# 的新手,请问有人可以建议如何解决这样的请求吗?

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 的最后一个参数传入。