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?

LeopardSkinPillBoxHat
  • 28,915
  • 15
  • 75
  • 111

2 Answers2

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; } }
Community
  • 1
  • 1
nmat
  • 7,430
  • 6
  • 30
  • 43
  • 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 Apr 22 '13 at 01:07
  • @LeopardSkinPillBoxHat I've updated the answer, check if it fixes the problem – nmat Apr 22 '13 at 01:16
  • No, I get the same error "Cannot serialize member 'ApproxPrice' of type System.Nullable`1[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 Apr 22 '13 at 01:40
  • @LeopardSkinPillBoxHat If that doesn't work, I added another suggestion to the answer – nmat Apr 22 '13 at 01:55
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.

Matt
  • 6,787
  • 11
  • 65
  • 112