1

I have the following class structure I want to serialize to XML:

public class Foo
{
    [XmlArray("approxPriceList")]
    [XmlArrayItem("approxPrice")]
    public List<ApproxPriceElement> ApproxPriceList { get; set; }
}

public class ApproxPriceElement
{
    [XmlAttribute("currency")]
    public string Currency { get; set; }

    [XmlElement("approxPrice")]
    public decimal? ApproxPrice { get; set; }
}

If I serialize Foo, I get the following XML:

<approxPriceList>
    <approxPrice currency="aud">
        <approxPrice>2220.00</approxPrice>
    </approxPrice>
</approxPriceList>

What I want is the following:

<approxPriceList>
    <approxPrice currency="aud">2220.00</approxPrice>
</approxPriceList>

One thought was to change ApproxPriceList in Foo to be a List<decimal?> but then I can't figure out how to associate a currency attribute with each approxPrice in the list.

Is there a way to achieve this?

2 Answers 2

1

Use XmlText instead of XmlElement("approxPrice")

[XmlText]
public decimal? ApproxPrice { get; set; }

To allow the Element to be null add this:

[XmlArrayItem("approxPrice", IsNullable = true)]
public List<ApproxPriceElement> ApproxPriceList { get; set; }

Here's a suggested work around for the "cannot be used to encode complex types" exception (source):

  [XmlText]
  public decimal ApproxPrice { get; set; }
  [XmlIgnore]
  public bool ApproxPriceSpecified { get { return ApproxPrice >= 0; } }
Sign up to request clarification or add additional context in comments.

4 Comments

This seems to work only if I use a non-nullable value, i.e. decimal instead of decimal?. Otherwise I get an exception when I serialize, indicating that XmlText cannot be used on complex types.
@LeopardSkinPillBoxHat I've updated the answer, check if it fixes the problem
No, I get the same error "Cannot serialize member 'ApproxPrice' of type System.Nullable1[System.Decimal]. XmlAttribute/XmlText cannot be used to encode complex types.". I think I have a workaround: (1) Change the property to non-nullable; (2) Add a ShouldSerializeApproxPrice` method to the ApproxPrice property, returning false if the property is set to a special value (e.g. -1).
@LeopardSkinPillBoxHat If that doesn't work, I added another suggestion to the answer
0

You could use IXmlSerializable:

public class Foo : IXmlSerializable
    {
        public Foo()
        {
            ApproxPriceList = new List<ApproxPriceElement>();
        }

        public List<ApproxPriceElement> ApproxPriceList { get; set; }

        public XmlSchema GetSchema()
        {
            return null;
        }

        public void ReadXml(XmlReader reader)
        {
            while (reader.Read())
            {
                if (reader.IsStartElement())
                {
                    switch (reader.LocalName)
                    {
                        case "approxPrice":
                            {
                                var approxPrice = new ApproxPriceElement();

                                approxPrice.Currency = reader.GetAttribute("currency");

                                var approxPriceValue = reader.ReadElementContentAsString();

                                if (!string.IsNullOrEmpty(approxPriceValue))
                                {
                                    approxPrice.ApproxPrice = decimal.Parse(approxPriceValue);
                                }

                                ApproxPriceList.Add(approxPrice);
                            }
                            break;
                    }
                }
            }
        }

        public void WriteXml(XmlWriter writer)
        {
            writer.WriteStartElement("approxPriceList");
            foreach (var approxPrice in ApproxPriceList)
            {
                writer.WriteStartElement("approxPrice");
                writer.WriteAttributeString("currency", approxPrice.Currency);
                writer.WriteString(approxPrice.ApproxPrice.ToString());
                writer.WriteEndElement();
            }
            writer.WriteEndElement();
        }
    }

    public class ApproxPriceElement
    {
        public string Currency { get; set; }

        public decimal? ApproxPrice { get; set; }
    }

It's not as convenient, but it does the job.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.