我可以在由 XSD C# 生成的 class 创建的 XML 中的同一标记处具有空属性和其他属性吗?

Can I have null attribute and other attribute at the same tag in XML created by XSD C# generated class?

我有一堆 C# classes,它们是从 XSD 自动生成的。然后我根据那些 C# classes 生成 XML 文件。到目前为止没有任何东西存在。

问题:

生成的 XML 文件正在接受验证,验证需要对所有带有 xsi:nil="true" 的 XML 标签添加额外的属性。基本上标签应该看起来像:<testTag.01 xsi:nil="true" NV="123123" />,但我无法在 C# 中实现它。我的代码是:

     if (myObject.TestTag.HasValue)
        {
            t.testTag01 = new testTag01();
            t.testTag01.Value = myObject.TestTag.Value;
        }
        //else
        //{
        //    t.testTag01 = new testTag01();
        //    t.testTag01.NV = "123123";//Not Recorded
        //}

此代码生成 <testTag.01>SomeValue</testTag.01><testTag.01 xsi:nil="true"/>

如果我取消注释 ELSE,结果将是:<testTag.01>SomeValue</testTag.01><testTag.01 NV="123123" />

所以我不知道如何获得验证工具所需的格式。有什么想法吗?

P.S.

这是自动生成的 C# class:

/// [System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.33440")] [System.SerializableAttribute()] [System.Diagnostics.DebuggerStepThroughAttribute()] [System.ComponentModel.DesignerCategoryAttribute("code")] [System.Xml.Serialization.XmlTypeAttribute(AnonymousType=true, Namespace="http://www.blabla.org")]

public partial class testTag01 {

private string nvField;

private SomeEnum valueField;

/// <remarks/>
[System.Xml.Serialization.XmlAttributeAttribute()]
public string NV {
    get {
        return this.nvField;
    }
    set {
        this.nvField = value;
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTextAttribute()]
public SomeEnum Value {
    get {
        return this.valueField;
    }
    set {
        this.valueField = value;
    }
} }

我不想改变那部分,但我知道不做是不可能的。我也尝试将 SomeEnum 设置为 Nullable。 public SomeEnum? Value,但抛出异常:

Cannot serialize member 'Value' of type System.Nullable`1[]. XmlAttribute/XmlText cannot be used to encode complex types.

XmlSerializer 不直接支持绑定到同时具有 xsi:nil="true" 和其他属性值的元素;参见 Xsi:nil Attribute Binding Support: The nil attribute and other attributes

因此,您需要手动发出属性。

如果您希望能够生成一个没有内容和两个属性的元素,一个名为 NV,另一个 alwaysxsi:nil="true",您可以修改您的 testTag01 class 以获得 NV 属性 以及具有正确命名空间和名称的合成 属性:

public class testTag01 
{
    [XmlAttribute]
    public string NV { get; set; }

    [XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
    public string Nil { get { return "true"; } set { } }
}

如果您有时想要xsi:nil="true"但有时又希望元素具有与您的SomeEnum相对应的内容,您需要做一些事情有点复杂,因为当元素有内容时 xsi:nil="true" 必须被抑制:

public class testTag01
{
    [XmlAttribute]
    public string NV { get; set; }

    [XmlAttribute("nil", Namespace = "http://www.w3.org/2001/XMLSchema-instance")]
    public string Nil { get { return SomeEnum == null ? "true" : null; } set { } }

    public bool ShouldSerializeNil() { return SomeEnum == null; }

    [XmlIgnore]
    public SomeEnum? SomeEnum { get; set; }

    [XmlText]
    public string SomeEnumText
    {
        get
        {
            if (SomeEnum == null)
                return null;
            return SomeEnum.Value.ToString();
        }
        set
        {
            // See here if one needs to parse XmlEnumAttribute attributes
            // 
            value = value.Trim();
            if (string.IsNullOrEmpty(value))
                SomeEnum = null;
            else
            {
                try
                {
                    SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, false);
                }
                catch (Exception)
                {
                    SomeEnum = (SomeEnum)Enum.Parse(typeof(SomeEnum), value, true);
                }
            }
        }
    }
}

(同时具有 xsi:nil="true" 和内容的元素将违反 XML standard;希望您没有。)

然后像这样使用它:

public class TestClass
{
    [XmlElement("testTag.01")]
    public testTag01 TestTag { get; set; }

    public static void Test()
    {
        Test(new TestClass { TestTag = new testTag01 { NV = "123123" } });
        Test(new TestClass { TestTag = new testTag01 { NV = "123123", SomeEnum = SomeEnum.SomeValue } });
    }

    private static void Test(TestClass test)
    {
        var xml = test.GetXml();

        var test2 = xml.LoadFromXML<TestClass>();

        Console.WriteLine(test2.GetXml());
        Debug.WriteLine(test2.GetXml());

        if (test2.TestTag.NV != test.TestTag.NV)
        {
            throw new InvalidOperationException("test2.TestTag.NV != test.TestTag.NV");
        }
    }
}

XML 输出如下:

<TestClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <testTag.01 NV="123123" xsi:nil="true" />
</TestClass>

<TestClass xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <testTag.01 NV="123123">SomeValue</testTag.01>
</TestClass>

原型 fiddle 使用这些扩展方法:

public static class XmlSerializationHelper
{
    public static T LoadFromXML<T>(this string xmlString, XmlSerializer serializer = null)
    {
        T returnValue = default(T);

        using (StringReader reader = new StringReader(xmlString))
        {
            object result = (serializer ?? new XmlSerializer(typeof(T))).Deserialize(reader);
            if (result is T)
            {
                returnValue = (T)result;
            }
        }
        return returnValue;
    }

    public static string GetXml<T>(this T obj, XmlSerializerNamespaces ns = null, XmlWriterSettings settings = null, XmlSerializer serializer = null)
    {
        using (var textWriter = new StringWriter())
        {
            settings = settings ?? new XmlWriterSettings() { Indent = true, IndentChars = "  " }; // For cosmetic purposes.
            using (var xmlWriter = XmlWriter.Create(textWriter, settings))
                (serializer ?? new XmlSerializer(typeof(T))).Serialize(xmlWriter, obj, ns);
            return textWriter.ToString();
        }
    }
}

正如预期的那样,没有开箱即用的解决方案,所以我即兴发挥了一下,在 post 处理逻辑中实现了我的目标。

我正在解析生成的 XML,如果我正在寻找具有 xsi:nil 属性但没有 NV 属性的节点 - 我添加具有默认值的 NV 属性。 对于具有 NV 属性但没有 xsi:nil.

的节点也是如此

代码如下:

        XmlDocument doc = new XmlDocument();// instantiate XmlDocument and load XML from file
        doc.Load("somepath.xml");

        //Get the nodes with NV attribute(using XPath) and add xsi:nill to that nodes
        XmlNodeList nodes = doc.SelectNodes("//*[@NV]");

        foreach (XmlNode node in nodes)
        {
            XmlAttribute nilAttr = doc.CreateAttribute("nil", "http://www.w3.org/2001/XMLSchema-instance");
            nilAttr.Value = "true";
            node.Attributes.Append(nilAttr);
        }

        //Get the nodes with xsi:nill attribute(using XPath) and add NV with default value to that nodes
        XmlNamespaceManager nsManager = new XmlNamespaceManager(doc.NameTable);
        nsManager.AddNamespace("xsi", "http://www.w3.org/2001/XMLSchema-instance");
        XmlNodeList nilNodes = doc.SelectNodes("//*[@xsi:nil]", nsManager);

        foreach (XmlNode node in nilNodes)
        {
            XmlAttribute nvAttr = doc.CreateAttribute("NV");
            nvAttr.Value = "7701003";
            node.Attributes.Append(nvAttr);
        }

        doc.Save("somepath.xml");

上面的答案完全有道理,但由于这些 类 是自动生成的,我将按照我的方式处理 post,因为如果提供商更改 XSD架构,我的解决方案不需要任何额外的工作。还是谢谢了。