Search code examples
c#xmlserializationxmlserializer

XML Serialization error - Invalid or missing value of the choice identifier 'ItemsElementName' of type 'ItemsChoiceType[]'


I am trying to serialize an object to be passed to a web service and getting the error above. I can see when debugging that the value is present in the object itself but it doesn't seem to be picking this up:

string[] tradeAreas = new string[] {"Area1", "Area2", "Area3", "Area4"};
//RetrieveMarketResultsFor
ItemsChoiceType[] choices = new ItemsChoiceType[] { ItemsChoiceType.area };
MarketResultIdentifier mri = new MarketResultIdentifier
{
    ItemsElementName = choices,
    Items = tradeAreas,
    ItemElementName = ItemChoiceType6.applyingDate,
    Item = DateTime.Today.AddDays(-1)
};

var ser = new XmlSerializer(typeof(MarketResultIdentifier));
using (var stream = new FileStream("mri.xml", FileMode.Create))
    ser.Serialize(stream, mri);

The web service generated code has the following classes used in the above code:

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.3130.0")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:openaccess")]
public partial class MarketResultIdentifier : OpenAccessAbstractObject {

    private string[] itemsField;

    private ItemsChoiceType[] itemsElementNameField;

    private object itemField;

    private ItemChoiceType6 itemElementNameField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("area", typeof(string))]
    [System.Xml.Serialization.XmlElementAttribute("areaNames", typeof(string))]
    [System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemsElementName")]
    public string[] Items {
        get {
            return this.itemsField;
        }
        set {
            this.itemsField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("ItemsElementName")]
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public ItemsChoiceType[] ItemsElementName {
        get {
            return this.itemsElementNameField;
        }
        set {
            this.itemsElementNameField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("applyingDate", typeof(System.DateTime), DataType="date")]
    [System.Xml.Serialization.XmlElementAttribute("auctionIdentification", typeof(AuctionIdentification))]
    [System.Xml.Serialization.XmlElementAttribute("deliveryDay", typeof(System.DateTime), DataType="date")]
    [System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemElementName")]
    public object Item {
        get {
            return this.itemField;
        }
        set {
            this.itemField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlIgnoreAttribute()]
    public ItemChoiceType6 ItemElementName {
        get {
            return this.itemElementNameField;
        }
        set {
            this.itemElementNameField = value;
        }
    }
}

/// <remarks/>
[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.3130.0")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:openaccess", IncludeInSchema=false)]
public enum ItemsChoiceType {

    /// <remarks/>
    area,

    /// <remarks/>
    areaNames,
}

[System.CodeDom.Compiler.GeneratedCodeAttribute("System.Xml", "4.7.3130.0")]
[System.SerializableAttribute()]
[System.Xml.Serialization.XmlTypeAttribute(Namespace="urn:openaccess", IncludeInSchema=false)]
public enum ItemChoiceType6 {

    /// <remarks/>
    applyingDate,

    /// <remarks/>
    auctionIdentification,

    /// <remarks/>
    deliveryDay,
}

It fails at line when trying ser.serialize....

Any help appreciated :)

Solution Turns out this was just down to me being stupid! Thanks to @dbc for pointing this out. My tradeAreas array had 4 items in there (i was using the wrong array here) which it should have only had one!


Solution

  • When you use XmlChoiceIdentifierAttribute to deserialize a sequence of choice elements into some shared data type (here, string), you need to add two arrays to your data model:

    1. A property returning an array of objects of appropriate type to capture the choice element contents, here the four string values in the tradeAreas array.

      This first array must be marked with [XmlChoiceIdentifier(name)] where name is the name of the second array, as well as [XmlElement(elementName)] attributes for each possible choice element name.

    2. And a property returning an array of enums to capture the choice element names, here <area> for each element.

      This array must be marked with [XmlIgnore].

    And finally, since the second array captures the element names for the element contents in the first array, the arrays must be in 1-1 correspondence. Thus choices must be initialized to the same length as tradeAreas, e.g. as follows:

    var tradeAreas = new string[] {"Area1", "Area2", "Area3", "Area4"};
    var choices = Enumerable.Repeat(ItemsChoiceType.area, tradeAreas.Length).ToArray();
    

    In your sample code tradeAreas has four entries while choices has but one, leading to the exception you see.

    Working sample .Net fiddle here.

    For documentation on binding to XML choice elements using XmlSerializer, see Choice Element Binding Support: Differentiating by Name as well as XmlChoiceIdentifierAttribute Class: Remarks. For a simple manual example, see e.g. this answer to How to Serialize List<T> property in c# without showing its type in XML serialization.