2

我正在尝试使用 XmlAttributeOverrides 来控制在类被序列化后哪些类属性出现在 xml 中。它适用于“根”类上的属性,但不适用于嵌套属性。这是一个简单的示例来说明我要完成的工作。

我的类层次结构如下:

public class Main
{
    public string Name { get; set; }
    public Location Address { get; set; }
}

public class Location
{
    public string StreetAddress { get; set; }
    public Contact ContactInfo{ get; set; }
}

public class Contact
{
    public string PhoneNumber { get; set; }
    public string EmailAddr { get; set; }
}

当我序列化 Main() 时,我得到如下信息:

<Main>
    <Name></Name>
    <Address>
        <StreetAddress></StreetAddress>
        <ContactInfo>
            <PhoneNumber></PhoneNumber>
            <EmailAddr></EmailAddr>
        </ContactInfo>
    </Address>
</Main>

我能做的是通过使用这个来防止出现名称或地址:

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("Address"));
overrides.Add(typeof(Main), "Address", attribs);
xs = new XmlSerializer(typeof(Main), overrides);

我还需要做的是让 Main.Address.ContactInfo 有时不被序列化如果它是空的)。我尝试了以下方法,但没有奏效:

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Contact), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Contact), overrides);

和...

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo "));
overrides.Add(typeof(Main.Address.ContactInfo), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main.Address.ContactInfo), overrides);

实际上,我已经尝试了很多,包括使用 XPath 语句来指定要定位的属性名称,但不想在尝试失败时填满此页面。通过这种方法,我要问的甚至是可能的吗?

4

3 回答 3

7

对于尝试使用 XmlAttributeOverrides 执行此操作的任何其他人,事实证明 @user1437872 非常接近找到答案。这是忽略嵌套元素 ContactInfo 的覆盖代码。

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
attribs.XmlElements.Add(new XmlElementAttribute("ContactInfo"));
overrides.Add(typeof(Address), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main), overrides);
于 2016-09-02T03:39:11.557 回答
4

有更简单的方法可以实现您正在寻找的东西。

您说您要实现的是/Main/Address/ContactInfo如果不ContactInfo包含数据则不序列化。

如果您保持代码不变,它将序列化 Main 的所有属性,无论它们是否为 null 或空。第一步,您需要为XmlSerializerNamespaces所有对象添加一个属性,否则每个空对象将被序列化为<myElement xsi:nil="true" />. 这可以很容易地完成,如下所示:

public MyXmlElement
{
    public MyXmlElement()
    {
        // Add your own default namespace to your type to prevet xsi:* and xsd:*
        // attributes from being generated.
        this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
            new XmlQualifiedName(string.Empty, "urn:myDefaultNamespace") });
    }

    [XmlElement("MyNullableProperty", IsNullable=false)]
    public string MyNullableProperty
    {
        get
        {
            return string.IsNullOrWhiteSpace(this._myNullableProperty) ? 
                null : this._myNullableProperty;
        }
        set { this._myNullableProperty = value; }
    }

    [XmlNamespacesDeclaration]
    public XmlSerializerNamespaces Namespaces { get { return this._namespaces; } }
    private XmlSerializerNamespaces _namespaces;
}

上面的代码声明了一个Namespaces属性,该属性包含 XML 对象的所有相关名称空间。您应该为所有对象提供一个默认命名空间(以上面的代码为模型)。这可以防止在序列化对象时为您的对象输出xsi:*和属性。xsd:*此外,使用 . 指定元素不可为空System.Xml.Serialization.XmlElementAttribute

此外,通过检查string.IsNullOrWhiteSpace(someVariable)并返回 null,则在完成上述操作后,该属性将不会被序列化。

所以,把这一切放在你的Location课堂上:

public class Location
{
    // You should have a public default constructor on all types for (de)sereialization.
    public Location()
    {
        this._namespaces = new XmlSerializerNamespaces(new XmlQualifiedName[] {
            new XmlQualifiedName(string.Empty, "urn:myNamespace"); // Default namespace -- prevents xsi:nil="true" from being generated, as well as xsd:* attributes.
        });
    }

    public string StreetAddress
    {
        // If you don't want <StreetAddress xsi:nil="true" /> to be generated, do this:
        get { return string.IsNullOrEmpty(this._streetAddress) ? null : this._streetAddress; }

        // Otherwise, if you don't care, just do
        // get;

        // Only need to implement setter if you don't want xsi:nil="true" to be generated.
        set { this._streetAddress = value; }

        // Otherwise, just
        // set;
    }
    private string _streetAddress;

    [XmlElement("ContactInfo", IsNullable=false)]
    public Contact ContactInfo
    {
        // You must definitely do this to prevent the output of ContactInfo element
        // when it's null (i.e. contains no data)
        get
        {
            if (this._contactInfo != null && string.IsNullOrWhiteSpace(this._contactInfo.PhoneNumber) && string.IsNullOrWhiteSpace(this._contactInfo.EmailAddr))
                return null;

             return this._contactInfo;
        }

        set { this._contactInfo = value; }
    }
    private Contact _contactInfo;

    [XmlNamespacesDeclarations]
    public XmlSerializerNamespaces Namespaces
    {
        get { return this._namespaces; }
    }
    private XmlSerializerNamespaces _namespaces;
}

Location通过对您的类进行这些更改,当所有属性都不是 null、空或空格,或者如果它本身为 nullContactInfo时,不应再将空属性序列化为 XML 。ContactInfo

您应该对其他对象进行类似的更改。

有关 .NET XML 序列化的更多信息,请参阅我的其他 stackoverflow 答案:

于 2013-05-13T16:50:59.657 回答
1

无需向属性添加 ContactInfo 元素

XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attribs = new XmlAttributes();
attribs.XmlIgnore = true;
overrides.Add(typeof(Address), "ContactInfo ", attribs);
xs = new XmlSerializer(typeof(Main), overrides);
于 2019-05-08T23:05:32.307 回答