【问题标题】:(De)serializing different root element names using one class使用一个类(反)序列化不同的根元素名称
【发布时间】:2015-09-13 19:02:11
【问题描述】:

我有几个具有不同根元素但子元素类型相同的 XML 文件,我希望能够创建一个类来保存不同的根元素,另一个类来保存每个子元素。 以下是 XML 文件外观的两个示例。

文件 1:

<?xml version="1.0" encoding="utf-8" ?>
<Sandra>
  <Address>
    <Street></Street>
    <Number></Number>
  </Address>
</Sandra>

文件 2:

<?xml version="1.0" encoding="utf-8" ?>
<John>
  <Address>
    <Street></Street>
    <Number></Number>
  </Address>
</John>

我希望能够仅使用 2 个类对其进行序列化和反序列化,例如:

[Serializable]
[XmlRoot]
public class Person
{
    [XmlElement("Address")]
    public List<Address> Adresses { get; set; }
}

[Serializable]
public class Address
{
    public string Street { get; set; }

    public string Number { get; set; }
}

我尝试使用以下方法阅读它们:

    var ser = new XmlSerializer(typeof(Person));
    var reader = XmlReader.Create("person1.xml");
    var person = (Person)ser.Deserialize(reader);

但我得到了"There is an error in XML document (2, 2).",因为反序列化器期待的是&lt;"Person"&gt; 根元素,而不是&lt;"John"&gt;&lt;"Paul"&gt;。如果我将 [XmlRoot] 更改为 [XmlRoot("John")] 效果很好,但这正是我在这里要避免的。

另外,我必须能够使用同样奇怪的 XML 结构将其序列化回来,因此我需要将根元素名称存储在 Person 类中。

我知道我可以简单地创建自己的(反)序列化器,但我想知道是否可以使用现有方法来实现它。

编辑 1:(回滚)。

Edit 2:回滚了“Edit 1”的修改,因为我找到了一种更简单的方法来实现我所需要的。请参阅下面的答案。

【问题讨论】:

    标签: c# xml-serialization


    【解决方案1】:

    找到了一种简洁快速的方法来解决我的问题! 在实例化 XmlSerializer 时,我只需要使用 XmlRootAttribute。这样我可以在运行时设置根元素的名称。

    var personsName = "Sandra";
    var ser = new XmlSerializer(typeof(Person), 
       new XmlRootAttribute { ElementName = personsName });
    var reader = XmlReader.Create("person1.xml");
    var person = (Person)ser.Deserialize(reader);
    

    当然,如果我想序列化它也一样。

    【讨论】:

      【解决方案2】:

      使用人名作为 XML 元素名感觉有点不靠谱。每个元素代表什么,JohnPerson

      如果您能够控制这些 XML 文件的形状,这样表示它们可能会更好:

      <?xml version="1.0" encoding="utf-8" ?>
      <Person name="Sandra">
        <Address>
          <Street></Street>
          <Number></Number>
        </Address>
      </Person>
      

      然后您将有一种简单的方法将名称存储在属性中,映射为 XML 属性:

      [Serializable]
      [XmlRoot]
      public class Person
      {
          [XmlElement("Address")]
          public List<Address> Adresses { get; set; }
      
          [XmlAttribute("name")]
          public string Name { get; set;}
      }
      

      【讨论】:

      • 是的,确实有点奇怪。但不是我改变XML结构,我只需要尊重这种格式的读写。
      【解决方案3】:

      在你的类中实现 IXmlSerializable 并在函数中按照你想要的方式进行序列化:

      ReadXml(System.Xml.XmlReader reader)WriteXml(System.Xml.XmlWriter writer)

      示例:

      [Serializable]
      public class Threshold : IXmlSerializable
      {
      
      public int Type {get;set;}
      public object Value {get;set;}
      public string Name {get;set;}
      public void ReadXml(System.Xml.XmlReader reader)
      {
      
      
      XElement thresholdXML = XElement.Load(reader);
      
      if (!thresholdXML.HasElements || thresholdXML.IsEmpty)
      return;
      
      Type = (ThresholdType)int.Parse(thresholdXML.Element("Type").Value);
      Value = Type.Equals(ThresholdType.Complex) ? thresholdXML.Element("Value").Value : (object)Decimal.Parse(thresholdXML.Element("Value").Value);
      Name = thresholdXML.Element("Name").Value;
      
      
      }
      
      
      public System.Xml.Schema.XmlSchema GetSchema()
      {
      return null;
      }
      
      public void WriteXml(System.Xml.XmlWriter writer)
      {
      XmlSerializerNamespaces xmlnsEmpty = new XmlSerializerNamespaces();
      xmlnsEmpty.Add("", "");
      
      
      writer.WriteElementString("Type", ((int)Type).ToString("D"));
      writer.WriteElementString("Value", Value.ToString());
      writer.WriteElementString("Name", Name);
      }
      }
      

      【讨论】:

      • 我尝试实现IXmlSerializable,但在尝试反序列化第一个存档时,我遇到了同样的错误{"&lt;Sandra xmlns=''&gt; was not expected."}。该类的 ReadXml() 方法甚至没有被调用。
      • 首先,您不需要任何 [XmlRoot] 或 [XmlElement] 属性。还可以尝试删除现有的 XML 并序列化新的 XML。在 ReadXML() 和 WriteXML() 上放置一个断点以查看它们是否被调用。关注调试器并向我更新异常的详细信息及其发生位置。
      • 按照您告诉我的操作并在尝试反序列化时遇到同样的错误:{"&lt;Sandra xmlns=''&gt; was not expected."}。 ReadXML 仍未被调用,可能是因为反序列化程序希望类名与根元素具有相同的名称存在[XmlRoot("rootname")] 属性。
      【解决方案4】:

      使用根元素作为ArrayOfClassName,然后尝试一下。

      <ArrayOfAlarmSummary>
        <AlarmSummary>
          <ClientId>1</ClientId>
          <StationId>2</StationId>
          <StationName>Station-2</StationName>
          <DateTime>13/09/15</DateTime>
          <TagName>AI2_2</TagName>
          <Description>TR DC Current</Description>
          <Units>Amps</Units>
          <NormalOperation>10 to 100</NormalOperation>
          <AlarmValue>132.48</AlarmValue>
          <AlarmDescription>
          </AlarmDescription>
        </AlarmSummary>
        <AlarmSummary>
          <ClientId>1</ClientId>
          <StationId>2</StationId>
          <StationName>Station-2</StationName>
          <DateTime>13/09/15</DateTime>
          <TagName>AI2_2</TagName>
          <Description>TR AC Current</Description>
          <Units>Amps</Units>
          <NormalOperation>10 to 100</NormalOperation>
          <AlarmValue>132.48</AlarmValue>
          <AlarmDescription>
          </AlarmDescription>
        </AlarmSummary>
      </ArrayOfAlarmSummary>
      

      这是反序列化类,使用它可以反序列化您的 Xml 文件。

       public class SerializeDeserialize<T>
          {
              StringBuilder sbData;
              StringWriter swWriter;
              XmlDocument xDoc;
              XmlNodeReader xNodeReader;
              XmlSerializer xmlSerializer;
              public SerializeDeserialize()
              {
                  sbData = new StringBuilder();
              }
              public string SerializeData(T data)
              {
                  XmlSerializer employeeSerializer = new XmlSerializer(typeof(T));
                  swWriter = new StringWriter(sbData);
                  employeeSerializer.Serialize(swWriter, data);
                  return sbData.ToString();
              }
      
              public T DeserializeData(string dataXML)
              {
                  xDoc = new XmlDocument();
                  xDoc.LoadXml(dataXML);
                  xNodeReader = new XmlNodeReader(xDoc.DocumentElement);
                  xmlSerializer = new XmlSerializer(typeof(T));
                  var employeeData = xmlSerializer.Deserialize(xNodeReader);
                  T deserializedEmployee = (T)employeeData;
                  return deserializedEmployee;
              }
          }
      

      调用类

       var appDomain = System.IO.Directory.GetCurrentDirectory() + "\\AlarmFiles";
                  var strPath = Path.Combine(appDomain,
                      "Alarm_" + DateTime.Now.ToString("ddMMyyyy") + Constants.FileType.XmlFile);
                  var fi = new FileInfo(strPath);
                  if (fi.Exists)
                  {
                      try
                      {
                          var xmlString = System.IO.File.ReadAllText(strPath);
                          var serializeAlarmSummary =
                              new SerializeDeserialize<List<AlarmSummary>>();
                          return serializeAlarmSummary.DeserializeData(xmlString);
                      }
                      catch (Exception ex)
                      {
                          throw ex;
                      }
      
                  }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-10-08
        • 1970-01-01
        • 2012-04-14
        • 1970-01-01
        • 1970-01-01
        • 2017-02-07
        相关资源
        最近更新 更多