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

时间:2021-06-27 17:18:01

I have a bunch of C# classes, which are auto generated from an XSD. Then I generate XML files based on those C# classes. Nothing existing so far.

我有一堆C#类,它们是从XSD自动生成的。然后我基于那些C#类生成XML文件。到目前为止还没有任何东西

The problem:

问题:

The generated XML files are going through validation and the validation requires an extra attribute to all XML tags with xsi:nil="true". Basically the tags should look like : <testTag.01 xsi:nil="true" NV="123123" />, but I can't achieve that in C#. My code is:

生成的XML文件正在进行验证,验证需要使用xsi:nil =“true”的所有XML标记的额外属性。基本上标签应该如下所示: ,但我无法在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
        //}

This code generates <testTag.01>SomeValue</testTag.01> or <testTag.01 xsi:nil="true"/>.

此代码生成 SomeValue

If I uncomment the ELSE, the result would be: <testTag.01>SomeValue</testTag.01> or <testTag.01 NV="123123" />.

如果我取消注释ELSE,结果将是: SomeValue

So I have no idea how to get to the format, which is required by the validation tool. Any ideas ?

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

P.S.

附:

Here is the auto-generated C# class:

这是自动生成的C#类:

/// [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")]

/// [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 {

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;
    }
} }

I wouldn't like to alter that part, but I understand it is impossible without doing it. Also I have tried to set SomeEnum to be Nullable. public SomeEnum? Value, but is throwing an exception:

我不想改变这一部分,但我知道如果不这样做就不可能。我也尝试将SomeEnum设置为Nullable。公共SomeEnum?价值,但抛出异常:

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

2 个解决方案

#1


1  

XmlSerializer doesn't directly support binding to elements that simultaneously have xsi:nil="true" along with other attribute values; see Xsi:nil Attribute Binding Support: The nil attribute and other attributes.

XmlSerializer不直接支持绑定到同时具有xsi:nil =“true”的元素以及其他属性值;请参阅Xsi:nil属性绑定支持:nil属性和其他属性。

Thus, you need to emit the attribute manually.

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

If you want to be able to generate an element with no content and two attributes, one named NV and the other always being xsi:nil="true", you can modify your testTag01 class to have the NV property as well as a synthetic property having the correct namespace and name:

如果您希望能够生成一个没有内容和两个属性的元素,一个名为NV而另一个属于xsi:nil =“true”,则可以修改testTag01类以获得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 { } }
}

If you sometimes want to have xsi:nil="true" but at other times want the element to have content corresponding to your SomeEnum, you need to do something a bit more complicated, since the xsi:nil="true" must be suppressed when the element has content:

如果你有时想要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
            // http://*.com/questions/3047125/retrieve-enum-value-based-on-xmlenumattribute-name-value
            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);
                }
            }
        }
    }
}

(An element that simultaneously has both xsi:nil="true" and content would be a violation of the XML standard; hopefully you don't have that.)

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

Then use it like:

然后使用它像:

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");
        }
    }
}

The XML output looks like:

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>

Or

要么

<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>

Prototype fiddle using these extension methods:

使用这些扩展方法的原型小提琴:

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();
        }
    }
}

#2


0  

As expected there is no solution for that case out of the box, so I improvise a bit and achieved my goal in a post processing logic.

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

I am parsing the generated XML and if I am looking for a node with xsi:nil attribute, but without NV attribute - I add NV attribute with default value. Same for the nodes with NV attribute, but no xsi:nil.

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

Here is the code:

这是代码:

        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");

The upper answer makes totally sense, but since these classes are auto-generated I will do it my way with the post processing, cause if the provider changes the XSD schema, my solution doesn't need any extra work. Thanks anyway.

上面的答案是完全有道理的,但由于这些类是自动生成的,我会按照后处理的方式进行,因为如果提供者更改XSD架构,我的解决方案不需要任何额外的工作。不管怎么说,还是要谢谢你。

#1


1  

XmlSerializer doesn't directly support binding to elements that simultaneously have xsi:nil="true" along with other attribute values; see Xsi:nil Attribute Binding Support: The nil attribute and other attributes.

XmlSerializer不直接支持绑定到同时具有xsi:nil =“true”的元素以及其他属性值;请参阅Xsi:nil属性绑定支持:nil属性和其他属性。

Thus, you need to emit the attribute manually.

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

If you want to be able to generate an element with no content and two attributes, one named NV and the other always being xsi:nil="true", you can modify your testTag01 class to have the NV property as well as a synthetic property having the correct namespace and name:

如果您希望能够生成一个没有内容和两个属性的元素,一个名为NV而另一个属于xsi:nil =“true”,则可以修改testTag01类以获得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 { } }
}

If you sometimes want to have xsi:nil="true" but at other times want the element to have content corresponding to your SomeEnum, you need to do something a bit more complicated, since the xsi:nil="true" must be suppressed when the element has content:

如果你有时想要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
            // http://*.com/questions/3047125/retrieve-enum-value-based-on-xmlenumattribute-name-value
            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);
                }
            }
        }
    }
}

(An element that simultaneously has both xsi:nil="true" and content would be a violation of the XML standard; hopefully you don't have that.)

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

Then use it like:

然后使用它像:

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");
        }
    }
}

The XML output looks like:

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>

Or

要么

<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>

Prototype fiddle using these extension methods:

使用这些扩展方法的原型小提琴:

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();
        }
    }
}

#2


0  

As expected there is no solution for that case out of the box, so I improvise a bit and achieved my goal in a post processing logic.

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

I am parsing the generated XML and if I am looking for a node with xsi:nil attribute, but without NV attribute - I add NV attribute with default value. Same for the nodes with NV attribute, but no xsi:nil.

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

Here is the code:

这是代码:

        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");

The upper answer makes totally sense, but since these classes are auto-generated I will do it my way with the post processing, cause if the provider changes the XSD schema, my solution doesn't need any extra work. Thanks anyway.

上面的答案是完全有道理的,但由于这些类是自动生成的,我会按照后处理的方式进行,因为如果提供者更改XSD架构,我的解决方案不需要任何额外的工作。不管怎么说,还是要谢谢你。