xsd.exe 生成了一个数组中包含多个元素的 c#

xsd.exe generated c# with multiple elements in an array

我有一组 XML 模式文件提供给我。我无法更改 XML,因为它们会不时更新。我正在使用 xsd.exe 将架构文件转换为生成的 C# 代码。我不能使用任何第三方工具。 XML 模式文件之一的一部分显示如下:

<xs:complexType name="LocationType">
  <xs:choice minOccurs="1" maxOccurs="1">
    <xs:element name="LocNum" minOccurs="1" maxOccurs="1" type="xs:string" />
    <xs:sequence>
      <xs:element name="Name" minOccurs="0" maxOccurs="1" type="xs:string" />
      <xs:element name="Address" minOccurs="0" maxOccurs="1" type="xs:string" />
      <xs:element name="City" minOccurs="1" maxOccurs="1" type="xs:string" />
      <xs:element name="State" minOccurs="0" maxOccurs="1">
    </xs:sequence>
  </xs:choice>
</xs:complexType>

转换为 c# 后,我得到如下结果:

[System.CodeDom.Compiler.GeneratedCodeAttribute("xsd", "4.0.30319.1")]
[System.SerializableAttribute()]
[System.Diagnostics.DebuggerStepThroughAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(Namespace = "http://abcxyz.com")]
public partial class LocationType
{

    private object[] itemsField;

    private ItemsChoiceType[] itemsElementNameField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Address", typeof(string), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlElementAttribute("City", typeof(string), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlElementAttribute("LocNum", typeof(string), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlElementAttribute("Longitude", typeof(decimal), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlElementAttribute("State", typeof(LocationTypeState), Form = System.Xml.Schema.XmlSchemaForm.Unqualified)]
    [System.Xml.Serialization.XmlChoiceIdentifierAttribute("ItemsElementName")]
    public object[] 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; }
    }
}

由于这些都是部分生成的 类,我可以随意添加额外的代码。我需要能够 read/set 个人属性,例如名称、地址、城市等

我需要能够序列化这些对象以匹配架构。

在 C# 中有没有一种方法可以创建一个 public 属性 来以正确的顺序读取或设置 Items 数组中的值等?即:

public partial class LocationType
{
    public string Address
    {
        get
        {
            // code here to return the correct Items[] element
        }
        set
        {
            // code here to set the correct Items[] element
        }
    }
}

该架构说明的是,如果某些外部类型包含类型 LocationType 的元素,人们会期望在内部找到 either

1) 子元素 <LocNum>, OR

2) 这些子元素依次为:<Name><Address><City><State>.

因此这里的数据是多态的,即使它没有在 xsd.exe 生成的 c# classes 中明确建模。这种方式是有道理的——一个位置可能被显式指定,或者间接指定为 table 中的查找。

像这样反序列化多态序列时,XmlSerializer 将它找到的每个元素放入与序列中的元素对应的数组字段中,在本例中为数组 Items。此外,应该有另一个相应的数组字段,由装饰 Items 数组的 XmlChoiceIdentifierAttribute attribute, in this case ItemsElementName. The entries in this array must needs be in 1-1 correspondence with the Items array. It records the name of the element that was deserialized in each index of the Items array, by way of the ItemsChoiceType enumeration, whose enum names must match the names in the XmlElementAttribute 属性标识。这样可以知道多态数据的具体选择。

因此,要完善 LocationType class 的实施,您需要确定给定的 LocationType 是直接的还是间接的;取出各种属性;并为每种类型(直接或间接)设置所有必需的数据。

这是它的原型。 (你没有在你的问题中包含 LocationTypeState 的定义,所以我只是把它当作一个字符串):

public partial class LocationType
{
    public LocationType() { }

    public LocationType(string locNum)
    {
        SetIndirectLocation(locNum);
    }

    public LocationType(string name, string address, string city, string state)
    {
        SetDirectLocation(name, address, city, state);
    }

    public bool IsIndirectLocation
    {
        get
        {
            return Array.IndexOf(ItemsElementName, ItemsChoiceType.LocNum) >= 0;
        }
    }

    public string Address { get { return (string)XmlPolymorphicArrayHelper.GetItem(Items, ItemsElementName, ItemsChoiceType.Address); } }

    public string LocNum { get { return (string)XmlPolymorphicArrayHelper.GetItem(Items, ItemsElementName, ItemsChoiceType.LocNum); } }

    // Other properties as desired.

    public void SetIndirectLocation(string locNum)
    {
        if (string.IsNullOrEmpty(locNum))
            throw new ArgumentException();
        object[] newItems = new object[] { locNum };
        ItemsChoiceType [] newItemsElementName = new ItemsChoiceType [] { ItemsChoiceType.LocNum };
        this.Items = newItems;
        this.ItemsElementName = newItemsElementName;
    }

    public void SetDirectLocation(string name, string address, string city, string state)
    {
        // In the schema, "City" is mandatory, others are optional.
        if (string.IsNullOrEmpty(city))
            throw new ArgumentException();
        List<object> newItems = new List<object>();
        List<ItemsChoiceType> newItemsElementName = new List<ItemsChoiceType>();
        if (name != null)
        {
            newItems.Add(name);
            newItemsElementName.Add(ItemsChoiceType.Name);
        }
        if (address != null)
        {
            newItems.Add(address);
            newItemsElementName.Add(ItemsChoiceType.Address);
        }
        newItems.Add(city);
        newItemsElementName.Add(ItemsChoiceType.City);
        if (state != null)
        {
            newItems.Add(state);
            newItemsElementName.Add(ItemsChoiceType.State);
        }
        this.Items = newItems.ToArray();
        this.ItemsElementName = newItemsElementName.ToArray();
    }
}

public static class XmlPolymorphicArrayHelper
{
    public static TResult GetItem<TIDentifier, TResult>(TResult[] items, TIDentifier[] itemIdentifiers, TIDentifier itemIdentifier)
    {
        if (itemIdentifiers == null)
        {
            Debug.Assert(items == null);
            return default(TResult);
        }
        Debug.Assert(items.Length == itemIdentifiers.Length);
        var i = Array.IndexOf(itemIdentifiers, itemIdentifier);
        if (i < 0)
            return default(TResult);
        return items[i];
    }
}

这是我们根据原始答案得出的最终解决方案。此静态 class 用于获取和设置适当的属性。

public static class XmlPolymorphicArrayHelper
{
    public static TResult GetItem<TIDentifier, TResult>(TResult[] items, TIDentifier[] itemIdentifiers, TIDentifier itemIdentifier)
    {
        if (itemIdentifiers == null)
        {
            return default(TResult);
        }
        var i = Array.IndexOf(itemIdentifiers, itemIdentifier);
        return i < 0 ? default(TResult) : items[i];
    }

    public static void SetItem<TIDentifier, TResult>(ref TResult[] items, ref TIDentifier[] itemIdentifiers, TIDentifier itemIdentifier, TResult value)
    {
        if (itemIdentifiers == null)
        {
            itemIdentifiers = new[] { itemIdentifier };
            items = new[] { value };

            return;
        }

        var i = Array.IndexOf(itemIdentifiers, itemIdentifier);
        if (i < 0)
        {
            var newItemIdentifiers = itemIdentifiers.ToList();
            newItemIdentifiers.Add(itemIdentifier);
            itemIdentifiers = newItemIdentifiers.ToArray();

            var newItems = items.ToList();
            newItems.Add(value);
            items = newItems.ToArray();
        }
        else
        {
            items[i] = value;
        }

    }
}

然后像这样从部分 class 调用它们:

public partial class LocationType
{
    [XmlIgnore]
    public string Address
    {
        get
        {
            return (string)XmlPolymorphicArrayHelper.GetItem(Items, ItemsElementName, ItemsChoiceType.Address);
        }
        set
        {
            XmlPolymorphicArrayHelper.SetItem(ref this.itemsField, ref this.itemsElementNameField, ItemsChoiceType.Address, value);
        }
    }
}

这个 sets/creates Items 数组中的适当成员,我可以将它用于实现此模式的多个 classes。