简体   繁体   中英

C# Serializing XmlElement with XmlText

I have this XML format I need to replicate:

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

I'm currently using something like this to achieve what I want:

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

However, after serializing my Item I get:

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

How do I fix my CustomElement class so the value doesn't get lost?

You have several problems here:

  1. You are trying to serialize the XmlValue member as the element value for the element generated for an instance of CustomElement<T> , but you have defined it as a read-only expression-bodied member :

     public T XmlValue => Value; 

    XmlSerializer will only serialize public read/write properties and fields, so you must add a setter as well as a getter:

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

    Alternatively, to simplify your model you could eliminate XmlValue entirely and serialize Value directly by marking it with [XmlText] . XML serialization attributes can be applied to fields as well as properties.

  2. You are trying to suppress serialization of the <something> element when the underlying value is null using the {propertyName}Specified pattern . This pattern is primarily used for tracking whether or not an associated element is encountered, and so the xxxSpecified property must be marked with [XmlIgnore] :

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

    Or, since you don't actually need to track the presence of the <something> element, you could simplify your model by switching to the ShouldSerialize{PropertyName}() pattern:

     public bool ShouldSerializeXmlSomething() => Something.HasValue; 

    The differences between the two patterns are described in ShouldSerialize () vs Specified Conditional Serialization Pattern .

  3. If your Item type is going to be the root element of your XML document, you must mark it with [XmlRoot("item")] to make the root element's name be <item> :

     [XmlRoot("item")] [XmlType(TypeName = "ci")] public class Item { // Etc Etc } 
  4. In your c# code you have marked the XmlSomething property with [XmlElement] but in your XML something is shown as an element : <something>true</something> .

    If you really want it to be an element you must mark XmlSomething with [XmlElement] :

     [XmlElement("something")] public bool XmlSomething { get => Something != null && Something.Value; set => Something = value; } 
  5. In your question, you show an example of a <something> element with a non-boolean textual value:

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

    I reckon this is a typo in the question, but if not, you will need to redesign your CustomElement<T> type to make Something be a string .

Sample working Roslyn .Net fiddle , and a second with the suggested simplifications for CustomElement<T> , and a third where <something> appears as a child element.

The classes from the second fiddle look like:

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM