【问题标题】:Deserialize multiple but "different" XML elements with the same name through XmlSerializer class in C#通过 C# 中的 XmlSerializer 类反序列化多个但“不同”的同名 XML 元素
【发布时间】:2018-04-25 18:20:37
【问题描述】:

鉴于此 XML,我需要将其反序列化为一个类。我不确定如何区分不同类型但名称相同的两个“MealPeriod”元素。

<SalesSummary>
  <MealPeriod1>Lunch</MealPeriod1>
  <MealPeriod1Sales>6447.58</MealPeriod1Sales>
  <MealPeriod2>Dinner</MealPeriod2>
  <MealPeriod2Sales>12074.04</MealPeriod2Sales>
  <MealPeriod3>Late-Night</MealPeriod3>
  <MealPeriod3Sales>5156.90</MealPeriod3Sales>
  <MealPeriod4></MealPeriod4>
  <MealPeriod4Sales>0.00</MealPeriod4Sales>
  <MealPeriod1>
    <Interval>
      <Name>10:00am-11:00am</Name>
      <Checks>1</Checks>
      <Guests>1</Guests>
      <AvgCheck>$31.50</AvgCheck>
      <AvgGuest>$31.50</AvgGuest>
      <Sales>$31.50</Sales>
    </Interval>
    <Interval>
      <Name>11:00am-12:00pm</Name>
      <Checks>8</Checks>
      <Guests>29</Guests>
      <AvgCheck>$85.22</AvgCheck>
      <AvgGuest>$23.51</AvgGuest>
      <Sales>$681.75</Sales>
    </Interval>
    <Interval>
      <Name>12:00pm-01:00pm</Name>
      <Checks>27</Checks>
      <Guests>53</Guests>
      <AvgCheck>$48.76</AvgCheck>
      <AvgGuest>$24.84</AvgGuest>
      <Sales>$1,316.58</Sales>
    </Interval>
    <Interval>
      <Name>01:00pm-02:00pm</Name>
      <Checks>17</Checks>
      <Guests>35</Guests>
      <AvgCheck>$58.76</AvgCheck>
      <AvgGuest>$28.54</AvgGuest>
      <Sales>$999.00</Sales>
    </Interval>
    <Interval>
      <Name>02:00pm-03:00pm</Name>
      <Checks>22</Checks>
      <Guests>31</Guests>
      <AvgCheck>$38.99</AvgCheck>
      <AvgGuest>$27.67</AvgGuest>
      <Sales>$857.75</Sales>
    </Interval>
    <Interval>
      <Name>03:00pm-04:00pm</Name>
      <Checks>21</Checks>
      <Guests>44</Guests>
      <AvgCheck>$33.76</AvgCheck>
      <AvgGuest>$16.11</AvgGuest>
      <Sales>$709.00</Sales>
    </Interval>
    <Interval>
      <Name>04:00pm-05:00pm</Name>
      <Checks>32</Checks>
      <Guests>55</Guests>
      <AvgCheck>$57.88</AvgCheck>
      <AvgGuest>$33.67</AvgGuest>
      <Sales>$1,852.00</Sales>
    </Interval>
    <Totals>
      <Checks>128</Checks>
      <Guests>248</Guests>
      <AvgCheck>$50.37</AvgCheck>
      <AvgGuest>$26.00</AvgGuest>
      <Sales>$6,447.58</Sales>
    </Totals>
  </MealPeriod1>
  <MealPeriod2>
    <Interval>
      <Name>05:00pm-06:00pm</Name>
      <Checks>36</Checks>
      <Guests>71</Guests>
      <AvgCheck>$47.85</AvgCheck>
      <AvgGuest>$24.26</AvgGuest>
      <Sales>$1,722.75</Sales>
    </Interval>
    <Interval>
      <Name>06:00pm-07:00pm</Name>
      <Checks>40</Checks>
      <Guests>79</Guests>
      <AvgCheck>$49.01</AvgCheck>
      <AvgGuest>$24.81</AvgGuest>
      <Sales>$1,960.25</Sales>
    </Interval>
    <Interval>
      <Name>07:00pm-08:00pm</Name>
      <Checks>46</Checks>
      <Guests>82</Guests>
      <AvgCheck>$51.03</AvgCheck>
      <AvgGuest>$28.63</AvgGuest>
      <Sales>$2,347.29</Sales>
    </Interval>
    <Interval>
      <Name>08:00pm-09:00pm</Name>
      <Checks>53</Checks>
      <Guests>80</Guests>
      <AvgCheck>$42.04</AvgCheck>
      <AvgGuest>$27.85</AvgGuest>
      <Sales>$2,228.25</Sales>
    </Interval>
    <Interval>
      <Name>09:00pm-10:00pm</Name>
      <Checks>39</Checks>
      <Guests>68</Guests>
      <AvgCheck>$46.94</AvgCheck>
      <AvgGuest>$26.92</AvgGuest>
      <Sales>$1,830.50</Sales>
    </Interval>
    <Interval>
      <Name>10:00pm-11:00pm</Name>
      <Checks>39</Checks>
      <Guests>56</Guests>
      <AvgCheck>$50.90</AvgCheck>
      <AvgGuest>$35.45</AvgGuest>
      <Sales>$1,985.00</Sales>
    </Interval>
    <Totals>
      <Checks>253</Checks>
      <Guests>436</Guests>
      <AvgCheck>$47.72</AvgCheck>
      <AvgGuest>$27.69</AvgGuest>
      <Sales>$12,074.04</Sales>
    </Totals>
  </MealPeriod2>
  <MealPeriod3>
    <Interval>
      <Name>11:00pm-12:00am</Name>
      <Checks>35</Checks>
      <Guests>54</Guests>
      <AvgCheck>$35.80</AvgCheck>
      <AvgGuest>$23.20</AvgGuest>
      <Sales>$1,253.04</Sales>
    </Interval>
    <Interval>
      <Name>12:00am-01:00am</Name>
      <Checks>34</Checks>
      <Guests>45</Guests>
      <AvgCheck>$38.21</AvgCheck>
      <AvgGuest>$28.87</AvgGuest>
      <Sales>$1,299.16</Sales>
    </Interval>
    <Interval>
      <Name>01:00am-02:00am</Name>
      <Checks>23</Checks>
      <Guests>45</Guests>
      <AvgCheck>$40.30</AvgCheck>
      <AvgGuest>$20.60</AvgGuest>
      <Sales>$926.87</Sales>
    </Interval>
    <Interval>
      <Name>02:00am-03:00am</Name>
      <Checks>30</Checks>
      <Guests>59</Guests>
      <AvgCheck>$48.38</AvgCheck>
      <AvgGuest>$24.60</AvgGuest>
      <Sales>$1,451.33</Sales>
    </Interval>
    <Interval>
      <Name>03:00am-04:00am</Name>
      <Checks>1</Checks>
      <Guests>6</Guests>
      <AvgCheck>$226.50</AvgCheck>
      <AvgGuest>$37.75</AvgGuest>
      <Sales>$226.50</Sales>
    </Interval>
    <Totals>
      <Checks>123</Checks>
      <Guests>209</Guests>
      <AvgCheck>$41.93</AvgCheck>
      <AvgGuest>$24.67</AvgGuest>
      <Sales>$5,156.90</Sales>
    </Totals>
  </MealPeriod3>
</SalesSummary>

到目前为止,这是我的课。使用具有相同名称的多个 XmlElement 属性是行不通的,但我不知道如何以一种方式装饰类属性。任何指针将不胜感激。谢谢!

public partial class SalesSummary
{
[XmlElement("MealPeriod1")]
public List<SalesSummaryMealPeriod1> MealPeriod1List { get; set; }

[XmlElement("MealPeriod1")]
public string MealPeriod1 { get; set; }

[XmlElement("MealPeriod1Sales")]
public string MealPeriod1Sales { get; set; }

[XmlElement("MealPeriod2")]
public List<SalesSummaryMealPeriod2> MealPeriod2List { get; set; }

[XmlElement("MealPeriod2")]
public string MealPeriod2 { get; set; }

[XmlElement("MealPeriod2Sales")]
public string MealPeriod2Sales { get; set; }

[XmlElement("MealPeriod3")]
public List<SalesSummaryMealPeriod3> MealPeriod3List { get; set; }

[XmlElement("MealPeriod3")]
public string MealPeriod3 { get; set; }

[XmlElement("MealPeriod3Sales")]
public string MealPeriod3Sales { get; set; }

[XmlElement("MealPeriod4")]
public List<SalesSummaryMealPeriod4> MealPeriod4List { get; set; }

[XmlElement("MealPeriod4")]
public string MealPeriod4 { get; set; }

[XmlElement("MealPeriod4Sales")]
public string MealPeriod4Sales { get; set; }
}

注意MealPeriodx 元素都是固定的。

【问题讨论】:

  • 您可能需要“手动”反序列化,方法是使用您最喜欢的解析器递归扫描 xml,并根据需要填充模型。使用这种方法,很容易区分具有内容的元素和具有子元素的元素。
  • &lt;MealPeriodx&gt; 元素的最大数量是固定的还是无限的?
  • @dbc MealPeriodx 元素都是固定的。

标签: c# .net xml xmlserializer


【解决方案1】:

您的每个&lt;MealPeriodx&gt; 元素都包含混合内容,并且可能包含:

  • 一个字符串值;
  • &lt;Interval&gt; 元素的序列;
  • &lt;Totals&gt; 元素。

基于Stefan对Correct XML serialization and deserialization of "mixed" types in .NET的回答,您可以将这样的元素绑定到c#类型,如下所示:

public class MealPeriod
{
    // A polymorphic array of "mixed" types.
    // See https://stackoverflow.com/questions/2567414/correct-xml-serialization-and-deserialization-of-mixed-types-in-net
    // and the solution by Stefan, https://stackoverflow.com/users/307747/stefan
    // for why this works.
    [XmlElement("Interval", typeof(Interval))]
    [XmlElement("Totals", typeof(Totals))]
    [XmlText(typeof(string))]
    public List<object> Items { get; set; }
}

public class Interval
{
    public string Name { get; set; }
    public int Checks { get; set; }
    public int Guests { get; set; }
    public string AvgCheck { get; set; }
    public string AvgGuest { get; set; }
    public string Sales { get; set; }
}

public class Totals
{
    public int Checks { get; set; }
    public int Guests { get; set; }
    public string AvgCheck { get; set; }
    public string AvgGuest { get; set; }
    public string Sales { get; set; }
}

然后,您可以如下定义一个根对象SalesSummary,其中每个MealPeriodx 属性必须是一个MealPeriod 对象的集合,因为每个&lt;MealPeriodx&gt; 在源 XML:

public class SalesSummary
{
    [XmlElement("MealPeriod1")]
    public List<MealPeriod> MealPeriod1 { get; set; }

    [XmlElement("MealPeriod1Sales")]
    public decimal MealPeriod1Sales { get; set; }

    [XmlElement("MealPeriod2")]
    public List<MealPeriod> MealPeriod2 { get; set; }

    [XmlElement("MealPeriod2Sales")]
    public decimal MealPeriod2Sales { get; set; }

    [XmlElement("MealPeriod3")]
    public List<MealPeriod> MealPeriod3 { get; set; }

    [XmlElement("MealPeriod3Sales")]
    public decimal MealPeriod3Sales { get; set; }

    [XmlElement("MealPeriod4")]
    public List<MealPeriod> MealPeriod4 { get; set; }

    [XmlElement("MealPeriod4Sales")]
    public decimal MealPeriod4Sales { get; set; }
}

public static class MealPeriodExtensions
{
    public static string MealPeriodName(this IEnumerable<MealPeriod> mealPeriods)
    {
        if (mealPeriods == null)
            return null;
        return String.Concat(mealPeriods.Where(m => m.Items != null).SelectMany(m => m.Items).OfType<string>());
    }

    public static IEnumerable<T> MealPeriodItems<T>(this IEnumerable<MealPeriod> mealPeriods)
    {
        if (mealPeriods == null)
            return Enumerable.Empty<T>();
        return mealPeriods.Where(m => m.Items != null).SelectMany(m => m.Items).OfType<T>();
    }
}

然后,给定一个反序列化辅助方法,例如

public static class XmlSerializationHelper
{
    public static T LoadFromXml<T>(this string xmlString, XmlSerializer serial = null)
    {
        serial = serial ?? new XmlSerializer(typeof(T));
        using (var reader = new StringReader(xmlString))
        {
            return (T)serial.Deserialize(reader);
        }
    }
}

你可以这样做:

var summary = xml.LoadFromXml<SalesSummary>();

// Prints:
// MealPeriod1 name: "Lunch"
Console.WriteLine("MealPeriod1 name: \"{0}\"", summary.MealPeriod1.MealPeriodName());

// Prints 
// MealPeriod1 Total Sales: "$6,447.58"
Console.WriteLine("MealPeriod1 Total Sales: \"{0}\"", summary.MealPeriod1.MealPeriodItems<Totals>().Single().Sales);   

注意事项:

  • 如果您将SalesSummary 重新序列化为XML,则两个&lt;MealPeriodx&gt; 元素将移动以彼此相邻。除了implementing IXmlSerializable 或手动构造[XmlAnyElement] 代理数组(如this answer 所示)之外,没有简单的方法可以使用XmlSerializer 来避免这种情况。

    幸运的是,您的问题只要求反序列化。

  • Interval 和 Totals 有许多共同的属性,因此您可能希望它们共享一个共同的基类或接口。

示例工作 .Net fiddle here.

【讨论】:

    猜你喜欢
    • 2011-07-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多