XML 反序列化未将值加载到 class

XML deserialization not loading values into class

我有以下 XML,我为其自动生成了一个 class。

生成 class 后,我必须进行一些调整才能获得 PayrollEmployeePayrollDatePayrollEmployeePayrollCategoryWorkCode classes。由于我进行了调整,两个 classes 都已加载,但所有值均为空。除非在反序列化过程中使用它们,否则我不会收到任何错误。为什么我的值没有加载到 class?

<Payroll>
<StartDate>2015-02-22</StartDate>
<EndDate>2015-02-28</EndDate>
<PostedDate>2015-02-28</PostedDate>
<Employee EmployeeId="123456">
<EmployeePayStatus></EmployeePayStatus>
<PayrollStoreNumber>1111</PayrollStoreNumber>
<ReviewedDate></ReviewedDate>
<PayrollCategory Category="OT">
</PayrollCategory>
<PayrollCategory Category="DT">
</PayrollCategory>
<PayrollCategory Category="REGULAR">
<PayrollDate>2015-02-23</PayrollDate>
<Hours>5.97</Hours>
<WorkCode code="888">
<TotalHours>5.97</TotalHours>
</WorkCode>
<PayrollDate>2015-02-24</PayrollDate>
<Hours>6.07</Hours>
<WorkCode code="888">
<TotalHours>6.07</TotalHours>
</WorkCode>
<PayrollDate>2015-02-25</PayrollDate>
<Hours>6.00</Hours>
<WorkCode code="888">
<TotalHours>6.00</TotalHours>
</WorkCode>
<PayrollDate>2015-02-26</PayrollDate>
<Hours>8.44</Hours>
<WorkCode code="888">
<TotalHours>8.44</TotalHours>
</WorkCode>
<PayrollDate>2015-02-27</PayrollDate>
<Hours>7.90</Hours>
<WorkCode code="888">
<TotalHours>7.90</TotalHours>
</WorkCode>
</PayrollCategory>
<PayrollCategory Category="SICK">
</PayrollCategory>
<PayrollCategory Category="VACATION">
</PayrollCategory>
<PayrollCategory Category="HOLIDAY">
</PayrollCategory>
<PayrollCategory Category="RT">
</PayrollCategory>
<PayrollCategory Category="EO">
</PayrollCategory>
<PayrollCategory Category="RSA">
</PayrollCategory>
</Employee>
<Employee EmployeeId="987654">
<EmployeePayStatus></EmployeePayStatus>
<PayrollStoreNumber>1111</PayrollStoreNumber>
<ReviewedDate></ReviewedDate>
<PayrollCategory Category="OT">
</PayrollCategory>
<PayrollCategory Category="DT">
</PayrollCategory>
<PayrollCategory Category="REGULAR">
<PayrollDate>2015-02-23</PayrollDate>
<Hours>6.38</Hours>
<WorkCode code="888">
<TotalHours>6.38</TotalHours>
</WorkCode>
<PayrollDate>2015-02-24</PayrollDate>
<Hours>6.82</Hours>
<WorkCode code="888">
<TotalHours>6.82</TotalHours>
</WorkCode>
<PayrollDate>2015-02-25</PayrollDate>
<Hours>6.83</Hours>
<WorkCode code="888">
<TotalHours>6.83</TotalHours>
</WorkCode>
<PayrollDate>2015-02-26</PayrollDate>
<Hours>7.32</Hours>
<WorkCode code="888">
<TotalHours>7.32</TotalHours>
</WorkCode>
<PayrollDate>2015-02-27</PayrollDate>
<Hours>7.40</Hours>
<WorkCode code="888">
<TotalHours>7.40</TotalHours>
</WorkCode>
<PayrollDate>2015-02-28</PayrollDate>
<Hours>4.17</Hours>
<WorkCode code="888">
<TotalHours>4.17</TotalHours>
</WorkCode>
</PayrollCategory>
<PayrollCategory Category="SICK">
</PayrollCategory>
<PayrollCategory Category="VACATION">
</PayrollCategory>
<PayrollCategory Category="HOLIDAY">
</PayrollCategory>
<PayrollCategory Category="RT">
</PayrollCategory>
<PayrollCategory Category="EO">
</PayrollCategory>
<PayrollCategory Category="RSA">
</PayrollCategory>
</Employee>
</Payroll>

这是我正在使用的 class。

namespace PayrollProcessor.BL
{
    /// <remarks/>
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    [System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
    public class Payroll
    {

        private System.DateTime startDateField;

        private System.DateTime endDateField;

        private System.DateTime postedDateField;

        private PayrollEmployee[] employeeField;

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
        public System.DateTime StartDate
        {
            get
            {
                return this.startDateField;
            }
            set
            {
                this.startDateField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
        public System.DateTime EndDate
        {
            get
            {
                return this.endDateField;
            }
            set
            {
                this.endDateField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
        public System.DateTime PostedDate
        {
            get
            {
                return this.postedDateField;
            }
            set
            {
                this.postedDateField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute("Employee")]
        public PayrollEmployee[] Employee
        {
            get
            {
                return this.employeeField;
            }
            set
            {
                this.employeeField = value;
            }
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class PayrollEmployee
    {

        private string employeePayStatusField;

        private string payrollStoreNumberField;

        private string reviewedDateField;

        private PayrollEmployeePayrollCategory[] payrollCategoryField;

        private int employeeIdField;

        /// <remarks/>
        public string EmployeePayStatus
        {
            get
            {
                return this.employeePayStatusField;
            }
            set
            {
                this.employeePayStatusField = value;
            }
        }

        /// <remarks/>
        public string PayrollStoreNumber
        {
            get
            {
                return this.payrollStoreNumberField;
            }
            set
            {
                this.payrollStoreNumberField = value;
            }
        }

        /// <remarks/>
        public string ReviewedDate
        {
            get
            {
                return this.reviewedDateField;
            }
            set
            {
                this.reviewedDateField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute("PayrollCategory")]
        public PayrollEmployeePayrollCategory[] PayrollCategory
        {
            get
            {
                return this.payrollCategoryField;
            }
            set
            {
                this.payrollCategoryField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public int EmployeeId
        {
            get
            {
                return this.employeeIdField;
            }
            set
            {
                this.employeeIdField = value;
            }
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class PayrollEmployeePayrollCategory
    {
        private PayrollEmployeePayrollDate[] payrollEmployeePayrollDatesField;

        private string categoryField;

        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public string Category
        {
            get
            {
                return this.categoryField;
            }
            set
            {
                this.categoryField = value;
            }
        }

        [System.Xml.Serialization.XmlElementAttribute("PayrollDate")]
        public PayrollEmployeePayrollDate[] PayrollEmployeePayrollDates
        {
            get
            {
                return payrollEmployeePayrollDatesField;
            }
            set
            {
                payrollEmployeePayrollDatesField = value;
            }
        }

    }

    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class PayrollEmployeePayrollDate
    {
        private DateTime? paryrollDateField;

        private decimal? hours;

        private PayrollEmployeePayrollCategoryWorkCode[] payrollEmployeePayrollCategoryWorkCode;

        [System.Xml.Serialization.XmlElementAttribute("PayrollDate", typeof(System.DateTime), DataType = "date")]
        public DateTime? PayrollDate
        {
            get
            {
                return this.paryrollDateField;
            }
            set
            {
                this.paryrollDateField = value;
            }
        }

        [System.Xml.Serialization.XmlElementAttribute("Hours", typeof(decimal))]
        public decimal? Hours
        {
            get
            {
                return this.hours;
            }
            set
            {
                this.hours = value;
            }
        }

         [System.Xml.Serialization.XmlElementAttribute("WorkCode", typeof(PayrollEmployeePayrollCategoryWorkCode))]
        public PayrollEmployeePayrollCategoryWorkCode[] WorkCode
        {
            get
            {
                return payrollEmployeePayrollCategoryWorkCode;
            }
            set
            {
                payrollEmployeePayrollCategoryWorkCode = value;
            }
        }
    }


    /// <remarks/>
    [System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
    public partial class PayrollEmployeePayrollCategoryWorkCode
    {

        private decimal totalHoursField;

        private int codeField;

        /// <remarks/>
        [System.Xml.Serialization.XmlElementAttribute("TotalHours", typeof(decimal))]
        public decimal TotalHours
        {
            get
            {
                return this.totalHoursField;
            }
            set
            {
                this.totalHoursField = value;
            }
        }

        /// <remarks/>
        [System.Xml.Serialization.XmlAttributeAttribute()]
        public int code
        {
            get
            {
                return this.codeField;
            }
            set
            {
                this.codeField = value;
            }
        }
    }
}

我更改生成的 classes 的原因是当我循环浏览类别时,我只得到一个日期。正如 XML 所示,每个类别下最多可以有 7 个日期。

XML 来自第三方,我认为我无法让他们更改它。

问题是 XML 结构不再匹配您所拥有的 classes 的结构。

PayrollCategory 的 XML 看起来像这样:

<PayrollCategory Category="REGULAR">
<PayrollDate>2015-02-23</PayrollDate>
<Hours>5.97</Hours>
<WorkCode code="888">
<TotalHours>5.97</TotalHours>
</WorkCode>
<!-- ... -->

请注意,PayrollDateHoursWorkCode 元素是 PayrollCategory 的直接子元素。

但是 PayrollEmployeePayrollCategory 的 C# class 结构将所有这三个元素通过类型 PayrollEmployeePayrollDates 属性 移动到子元素 PayrollDate =22=]。 PayrollEmployeePayrollDate 定义 PayrollDateHoursWorkCode 属性(因此也将这些声明为 PayrollDate 的子元素)。

对于您现在拥有的 class 结构,相应的 XML 如下所示:

<PayrollCategory Category="REGULAR">
    <PayrollDate>
        <PayrollDate>2015-02-23</PayrollDate>
        <Hours>5.97</Hours>
        <WorkCode code="888">
        <TotalHours>5.97</TotalHours>
        </WorkCode>
    </PayrollDate>
    <!-- ... -->

因此,您必须调整 classes 以匹配现有的 XML,或者更改 XML 代以匹配新的 class如果这是你需要的结构。

更新:您无法更新 XML,并且您希望在 C# 中反映 PayrollDate/Hours/WorkCode 的分组 classes.

对此没有一个干净的解决方案,因为 .NET XML 序列化框架没有将一组元素映射到 class 的概念,尽管这是一个合理的 XML 习语(在 W3C XML 模式中表示为模型组 xs:group 定义)。

.NET XML 序列化的优点是它很容易做简单的事情,但缺点是它不能很好地扩展到更复杂的用例。

假设我正确理解了您的输入 XML 结构,您可以采取的一种方法是为每个 PayrollDateHoursWorkCode 存储数组在映射到 PayrollCategory 的 class 中的 "flat" 表示中。这些将用于 XML 序列化。然后,添加一个 属性,它根据这些 XML 映射的属性计算对象集合,以您想要的方式重组数据:

[System.Xml.Serialization.XmlElementAttribute("PayrollDate", typeof(System.DateTime), DataType = "date")]
public DateTime[] Xml_PayrollDates
{
    get;
    set;
}

[System.Xml.Serialization.XmlElementAttribute("Hours", typeof(decimal))]
public decimal[] Xml_Hours
{
    get;
    set;
}

[System.Xml.Serialization.XmlElementAttribute("WorkCode", typeof(PayrollEmployeePayrollCategoryWorkCode))]
public PayrollEmployeePayrollCategoryWorkCode[] Xml_WorkCodes
{
    get;
    set;
}

[System.Xml.Serialization.XmlIgnore()]
public PayrollEmployeePayrollDate[] PayrollEmployeePayrollDates
{
    get
    {
        if (Xml_PayrollDates == null || Xml_Hours == null || Xml_WorkCodes == null)
        {
            if (Xml_PayrollDates == null && Xml_Hours == null && Xml_WorkCodes == null)
            {
                return new PayrollEmployeePayrollDate[0];
            }
            else
            {
                throw new ApplicationException("Mismatched PayrollDate/Hours/WorkCode");
            }
        }

        if (Xml_PayrollDates.Length != Xml_WorkCodes.Length || Xml_WorkCodes.Length != Xml_Hours.Length)
        {
            throw new ApplicationException("Mismatched PayrollDate/Hours/WorkCode");
        }

        var dates = new PayrollEmployeePayrollDate[Xml_PayrollDates.Length];

        for(int i = 0; i < dates.Length; i++)
        {
            dates[i] = new PayrollEmployeePayrollDate
            {
                Hours = Xml_Hours[i],
                WorkCode = Xml_WorkCodes[i],
                PayrollDate = Xml_PayrollDates[i]
            };
        }

        return dates;
    }
}

.NET 将按照它们在您的 XML 中出现的顺序对元素进行反序列化,因此如果我正确解释了您的 XML 结构,您可以再次匹配所有内容,因为这些元素总是以相同的顺序出现在组中,并且如果其他元素存在,则组中的三个元素中的每一个都始终存在。这意味着索引 0 处的 Hours、索引 0 处的 WorkCode 和索引 0 处的 TotalHours 都可以打包到 PayrollEmployeePayrollDate.[=42 的单个实例中=]

我没有解决通过这个计算 属性 以相同 XML 格式回写的问题,但根据我对您的要求的理解,您只是在读取数据,因此不需要这样做.

这是完整的 classes 集,并进行了必要的调整以支持此方案(请注意 PayrollEmployeePayrollDate 已被修改,并且此 class 不再在XML连载):

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public class Payroll
{

    private System.DateTime startDateField;

    private System.DateTime endDateField;

    private System.DateTime postedDateField;

    private PayrollEmployee[] employeeField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
    public System.DateTime StartDate
    {
        get
        {
            return this.startDateField;
        }
        set
        {
            this.startDateField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
    public System.DateTime EndDate
    {
        get
        {
            return this.endDateField;
        }
        set
        {
            this.endDateField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute(DataType = "date")]
    public System.DateTime PostedDate
    {
        get
        {
            return this.postedDateField;
        }
        set
        {
            this.postedDateField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("Employee")]
    public PayrollEmployee[] Employee
    {
        get
        {
            return this.employeeField;
        }
        set
        {
            this.employeeField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class PayrollEmployee
{

    private string employeePayStatusField;

    private string payrollStoreNumberField;

    private string reviewedDateField;

    private PayrollEmployeePayrollCategory[] payrollCategoryField;

    private int employeeIdField;

    /// <remarks/>
    public string EmployeePayStatus
    {
        get
        {
            return this.employeePayStatusField;
        }
        set
        {
            this.employeePayStatusField = value;
        }
    }

    /// <remarks/>
    public string PayrollStoreNumber
    {
        get
        {
            return this.payrollStoreNumberField;
        }
        set
        {
            this.payrollStoreNumberField = value;
        }
    }

    /// <remarks/>
    public string ReviewedDate
    {
        get
        {
            return this.reviewedDateField;
        }
        set
        {
            this.reviewedDateField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("PayrollCategory")]
    public PayrollEmployeePayrollCategory[] PayrollCategory
    {
        get
        {
            return this.payrollCategoryField;
        }
        set
        {
            this.payrollCategoryField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public int EmployeeId
    {
        get
        {
            return this.employeeIdField;
        }
        set
        {
            this.employeeIdField = value;
        }
    }
}

/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class PayrollEmployeePayrollCategory
{
    private string categoryField;

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string Category
    {
        get
        {
            return this.categoryField;
        }
        set
        {
            this.categoryField = value;
        }
    }

    [System.Xml.Serialization.XmlElementAttribute("PayrollDate", typeof(System.DateTime), DataType = "date")]
    public DateTime[] Xml_PayrollDates
    {
        get;
        set;
    }

    [System.Xml.Serialization.XmlElementAttribute("Hours", typeof(decimal))]
    public decimal[] Xml_Hours
    {
        get;
        set;
    }

    [System.Xml.Serialization.XmlElementAttribute("WorkCode", typeof(PayrollEmployeePayrollCategoryWorkCode))]
    public PayrollEmployeePayrollCategoryWorkCode[] Xml_WorkCodes
    {
        get;
        set;
    }

    [System.Xml.Serialization.XmlIgnore()]
    public PayrollEmployeePayrollDate[] PayrollEmployeePayrollDates
    {
        get
        {
            if (Xml_PayrollDates == null || Xml_Hours == null || Xml_WorkCodes == null)
            {
                if (Xml_PayrollDates == null && Xml_Hours == null && Xml_WorkCodes == null)
                {
                    return new PayrollEmployeePayrollDate[0];
                }
                else
                {
                    throw new ApplicationException("Mismatched PayrollDate/Hours/WorkCode");
                }
            }

            if (Xml_PayrollDates.Length != Xml_WorkCodes.Length || Xml_WorkCodes.Length != Xml_Hours.Length)
            {
                throw new ApplicationException("Mismatched PayrollDate/Hours/WorkCode");
            }

            var dates = new PayrollEmployeePayrollDate[Xml_PayrollDates.Length];

            for(int i = 0; i < dates.Length; i++)
            {
                dates[i] = new PayrollEmployeePayrollDate
                {
                    Hours = Xml_Hours[i],
                    WorkCode = Xml_WorkCodes[i],
                    PayrollDate = Xml_PayrollDates[i]
                };
            }

            return dates;
        }
    }

}

public partial class PayrollEmployeePayrollDate
{
    public DateTime PayrollDate
    {
        get;
        set;
    }

    public decimal Hours
    {
        get;
        set;
    }

    public PayrollEmployeePayrollCategoryWorkCode WorkCode
    {
        get;
        set;
    }
}


/// <remarks/>
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
public partial class PayrollEmployeePayrollCategoryWorkCode
{

    private decimal totalHoursField;

    private int codeField;

    /// <remarks/>
    [System.Xml.Serialization.XmlElementAttribute("TotalHours", typeof(decimal))]
    public decimal TotalHours
    {
        get
        {
            return this.totalHoursField;
        }
        set
        {
            this.totalHoursField = value;
        }
    }

    /// <remarks/>
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public int code
    {
        get
        {
            return this.codeField;
        }
        set
        {
            this.codeField = value;
        }
    }
}

旁注:如果您使用 List<T> 而不是数组,您可能会发现 classes 更易于使用,并安排集合属性具有空集合的默认值 (所以你不必担心检查 属性 是否为空,如果没有子元素,集合是否为空。