C# 使用 XmlText 序列化 XmlElement

C# Serializing XmlElement with XmlText

我有这个 XML 格式需要复制:

<item>
    <attribute1>1</attribute1>
    <attribute2 something="true">
        2
    </attribute2>
    <attribute3 something="false">
        3
    </attribute3>
    <!-- goes on until attribute25 -->
</item>

我目前正在使用类似的东西来实现我想要的:

Item.cs:

[XmlType(TypeName = "item")]
public class Item {

    [XmlElement("attribute1")]
    public CustomElement<string> Attribute1 { get; set; }

    [XmlElement("attribute2")]
    public CustomElement<string> Attribute2 { get; set; }

    [XmlElement("attribute3")]
    public CustomElement<string> Attribute3 { get; set; }

    // Etc Etc
}

CustomElement.cs:

/// <summary>
///     Represents a CustomElement.
/// </summary>
/// <typeparam name="T">The type for the value of the element.</typeparam>

public class CustomElement<T>
{
    [XmlIgnore] public T Value;

    [XmlText]
    public T XmlValue => Value;

    public CustomElement()
    {
    }

    public CustomElement(T value)
    {
        Value = value;
    }

    [XmlIgnore]
    public bool? Something { get; set; }

    [XmlAttribute("something")]
    public bool XmlSomething
    {
        get => Something != null && Something.Value;
        set => Something = value;
    }

    public bool XmlSomethingSpecified => Something.HasValue;

    public static implicit operator CustomElement<T>(T x)
    {
        return new CustomElement<T>(x);
    }
}

但是,在序列化我的 Item 之后,我得到:

<item>
    <attribute1 />
    <attribute2 />
    <attribute3 />
</item>

如何修复我的 CustomElement class 以免值丢失?

你这里有几个问题:

  1. 您正在尝试将 XmlValue 成员序列化为为 CustomElement<T> 的实例生成的元素的元素值,但您已将其定义为 [=81] =]只读 expression-bodied member:

    public T XmlValue => Value;
    

    XmlSerializer 只会序列化 public read/write 属性和字段,因此您必须添加一个 setter 以及一个 getter:

    [XmlText]
    public T XmlValue { get => Value; set => Value = value; }
    

    或者,为了简化您的模型,您可以完全消除 XmlValue 并通过使用 [XmlText] 标记直接序列化 Value。 XML 序列化属性可以应用于字段和属性。

  2. 您正在尝试使用 {propertyName}Specified pattern 在基础值为 null 时抑制 <something> 元素的序列化。此模式主要用于跟踪是否遇到关联元素,因此xxxSpecified 属性必须用[XmlIgnore]标记:

    [XmlIgnore]
    public bool XmlSomethingSpecified => Something.HasValue;
    

    或者,由于您实际上不需要跟踪 <something> 元素的存在,您可以通过切换到 ShouldSerialize{PropertyName}() 模式来简化模型:

    public bool ShouldSerializeXmlSomething() => Something.HasValue;
    

    两种模式的区别在.

  3. 中描述
  4. 如果您的 Item 类型将成为您的 XML 文档的根元素,您必须用 [XmlRoot("item")] 标记它以创建根元素的名称成为 <item>:

    [XmlRoot("item")]
    [XmlType(TypeName = "ci")]
    public class Item
    {
        // Etc Etc
    }
    
  5. 在您的 c# 代码中,您已将 XmlSomething 属性 标记为 [XmlElement],但在您的 XML something 中显示为元素<something>true</something>

    如果你真的想让它成为一个元素,你必须用 [XmlElement] 标记 XmlSomething:

    [XmlElement("something")]
    public bool XmlSomething
    {
        get => Something != null && Something.Value;
        set => Something = value;
    }
    
  6. 在您的问题中,您展示了一个具有非布尔文本值的 <something> 元素的示例:

    <attribute3>
        3
        <something>text</something>
    </attribute3>
    

    我认为这是问题中的错字,但如果不是,您将需要重新设计 CustomElement<T> 类型,使 Something 成为 string.

工作示例 Roslyn .Net fiddle, and a second with the suggested simplifications for CustomElement<T>, and a third,其中 <something> 作为子元素出现。

第二个 fiddle 的 类 看起来像:

public class CustomElement<T>
{
    [XmlText]
    public T Value;

    public CustomElement()
    {
    }

    public CustomElement(T value)
    {
        Value = value;
    }

    [XmlIgnore]
    public bool? Something { get; set; }

    [XmlAttribute("something")]
    public bool XmlSomething
    {
        get => Something != null && Something.Value;
        set => Something = value;
    }

    public bool ShouldSerializeXmlSomething() => Something.HasValue;

    public static implicit operator CustomElement<T>(T x)
    {
        return new CustomElement<T>(x);
    }
}

[XmlRoot("item")]
[XmlType(TypeName = "ci")]
public class Item
{
    [XmlElement("attribute1")]
    public CustomElement<string> Attribute1 { get; set; }

    [XmlElement("attribute2")]
    public CustomElement<string> Attribute2 { get; set; }

    [XmlElement("attribute3")]
    public CustomElement<string> Attribute3 { get; set; }

    // Etc Etc
}