xsd.exe 生成的 c# 具有数组中的多个元素 [英] xsd.exe generated c# with multiple elements in an array

查看:17
本文介绍了xsd.exe 生成的 c# 具有数组中的多个元素的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I have a set of XML schema files provided to me. I cannot changed the XML as these will be updated on occasion. I am using xsd.exe to convert the schema files to generated c# code. I cannot use any third party tools. Part of one of the XML schema files appears as such:

<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# 时,我得到如下结果:

When converted to c#, I get a result such as this:

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

由于这些都生成为部分类,我可以随意添加其他代码.我需要能够读取/设置各个属性,例如姓名、地址、城市等.

Since these all get generated to partial classes, I am free to add additional code. I need to be able to read/set the individual properties, such as Name, Address, City, etc.

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

I need to be able to serialize these objects to match the schema.

在 C# 中有没有办法创建一个公共属性,该属性将以正确的顺序读取或设置 Items 数组中的值等?即:

Is there a way in c# to create a public property that will read or set the value in the Items array at the proper sequence, etc.? ie:

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 类型的元素,人们会期望在 内部找到要么

What that schema says is that if some outer type contains an element of type LocationType, one would expect to find inside either

1) 一个子元素<LocNum>,或

2) 这些子元素依次为:

><状态>.

2) These sub-elements in sequence: <Name>, <Address>, <City> and <State>.

因此,这里的数据是多态的,即使它没有在 xsd.exe 生成的 c# 类中明确建模.这种做法是有道理的——可以显式指定位置,也可以间接指定为在表中查找.

Thus the data here is polymorphic, even though it isn't being explicitly modeled as such in the c# classes generated by xsd.exe. This sort of makes sense -- a location might be specified explicitly, or indirectly as a look-up in a table.

当像这样反序列化多态序列时,XmlSerializer 将它找到的每个元素放入与序列中的元素相对应的数组字段中,在本例中为数组Items.此外,应该还有另一个对应的数组字段,由 XmlChoiceIdentifierAttribute 属性,在本例中为 ItemsElementName.此数组中的条目必须与 Items 数组一一对应.它通过 ItemsChoiceType 枚举记录在 Items 数组的每个索引中被反序列化的元素的名称,其枚举名称必须与 XmlElementAttribute 属性装饰 Items 数组.这样就可以知道多态数据的具体选择.

When deserializing a polymorphic sequence like this, XmlSerializer puts each element it finds in the array field corresponding to the elements in the sequence, in this case the array Items. In addition, there should be another corresponding array field identified by the 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 attributes decorating the Items array. This allows the specific choice of polymorphic data to be known.

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

Thus, to round out the implementation of your LocationType class, you will need to determine whether a given LocationType is direct or indirect; fetch various properties out; and for each type (direct or indirect), set all required data.

这是一个原型.(您的问题中没有包含 LocationTypeState 的定义,所以我只是将其视为字符串):

Here is a prototype of that. (You didn't include the definition for LocationTypeState in your question, so I'm just treating it as a string):

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

这篇关于xsd.exe 生成的 c# 具有数组中的多个元素的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆