【问题标题】:C# : manually reading XML config for derived classesC#:手动读取派生类的 XML 配置
【发布时间】:2010-01-06 16:13:43
【问题描述】:

假设我有 CarResource 类, 类 RaceCarResource:公共汽车资源, 和类 SuperDuperUltraRaceCarResource :公共 RaceCarResource。

我希望能够使用单个方法 LoadFromXML 加载他们的数据。

我将如何让 CarResource:LoadFromXML 加载它的数据, RaceCarResource 调用 CarResource:LoadFromXML 然后加载它自己的附加数据等?

如果我使用 XmlTextReader,我只知道如何一次性解析整个文件, 不是如何使用它,所以首先 CarResource:LoadFromXML 可以做它的事情,然后是 RaceCarResource 等等。

我希望我的意思至少有点清楚:)

【问题讨论】:

    标签: c# xml serialization manual derived


    【解决方案1】:
    public class CarResource
    {
        public virtual void LoadFromXML(String xmlData)
        {
            ...
        }
    }
    
    public class RaceCarResource : CarResource
    {
        public override void LoadFromXML(String xmlData)
        {
            base.LoadFromXML(xmlData);
            ...
        }
    }
    

    ...等等。 new 关键字将隐藏继承的方法,但仍允许从子类调用它。

    至于实际解析 XML,您有几个选择。我的第一个建议是将整个 XML 文件读入内存……然后使用 LINQ to XML 解析并填充您的类。您也可以尝试 XmlSerializer(LINQ to XML 更容易实现,但随着代码库大小的增长,Xml 序列化可以使维护更容易)。

    【讨论】:

    • 但是我如何真正读取xml文件,让CarResource只解析自己的数据,让RaceCarResource自己解析自己的数据呢?
    • 为什么不直接将基类的方法设为虚拟呢?然后,子类将根据需要覆盖虚拟调用,并且仍然能够调用基类实现。
    • @Chris - 视情况而定。我更新了我的答案,因为你是对的,在这种情况下,以这种方式构造类更有意义......但请查看此链接:msdn.microsoft.com/en-us/library/6fawty39.aspx
    • XML 反序列化不能在 Load 方法中使用并返回 void(除非您打算使用鸭子类型,这在此类层次结构场景中很疯狂)。您不能将反序列化的对象分配给“this”。因此,根据我的回复,您需要一个返回基类的实例方法(以获取要反序列化的当前类型)。
    【解决方案2】:

    您还可以根据要从中加载的 XML 文件的结构使用 XML 序列化。可以覆盖 load 方法(然后在后续类中覆盖)以加载特定信息 - 或仅使用属性。见:http://msdn.microsoft.com/en-us/library/ms950721.aspx

    【讨论】:

      【解决方案3】:

      您有几个选择。

      您可以使用 Linq to XML 查询子实体并将这些节点传递给您的其他类。这可能是最有效的方法。

      您可以使用 xmlnavigator,再次只传递适当的子节点... 见:Implementing my own XPathNavigator in C#

      你可以简单地使用xml序列化(XmlSerialize XmlDeserialize),见C# - How to xml deserialize object itself?

      【讨论】:

        【解决方案4】:

        为了使用 XML 反序列化,实例方法使当前对象有效地“不可变”,但我建议这样:

        public class CarResource
        {
            public CarResource LoadNewFromXML(string xml)
            {
                XmlSerializer ser = new XmlSerializer(this.GetType());
                object o = null;
                using (MemoryStream ms = new MemoryStream(Encoding.ASCII.GetBytes(xml)))
                {
                    o = ser.Deserialize(ms);
                }
                return o as CarResource;
            }
        }
        
        public class RaceCarResource : CarResource
        {
        }
        
        public class SuperRaceCarResource : RaceCarResource
        { 
        }
        

        您的调用代码如下所示:

        RaceCarResource car = new RaceCarResource();
        car = car.LoadNewFromXML("<RaceCarResource/>") as RaceCarResource;
        
        SuperRaceCarResource sc = new SuperRaceCarResource();
        sc = sc.LoadNewFromXML("<SuperRaceCarResource/>") as SuperRaceCarResource;
        

        【讨论】:

          【解决方案5】:

          如果您的 XML 与 .net XML 序列化不兼容,那么最简单的方法是创建一个工厂来检测 XML 代表的资源类型,然后适当地处理它。如果你想把解析放到你的对象中,那么在创建对象后使用虚拟方法来解析内部:

          class CarResource
          {
              public string Color { get; private set; }
          
              internal virtual void ReadFrom(XmlReader xml)
              {
                  this.Color = xml.GetAttribute("colour");
              }
          }
          
          class RaceCarResource : CarResource
          {
              public string Sponsor { get; private set; }
          
              internal override void ReadFrom(XmlReader xml)
              {
                  base.ReadFrom(xml);
                  this.Sponsor = xml.GetAttribute("name-on-adverts");
              }
          }
          
          class SuperDuperUltraRaceCarResource : RaceCarResource
          {
              public string Super { get; private set; }
          
              internal override void ReadFrom(XmlReader xml)
              {
                  base.ReadFrom(xml);
                  this.Super = xml.GetAttribute("soup");
              }
          }
          
          class CarResourceFactory
          {
              public CarResource Read(XmlReader xml)
              {
                  CarResource car;
          
                  switch (xml.LocalName)
                  {
                      case "ordinary-car": car = new CarResource(); break;
                      case "racecar": car = new RaceCarResource(); break;
                      case "super_duper": car = new SuperDuperUltraRaceCarResource(); break;
                      default: throw new XmlException();
                  }
          
                  XmlReader sub = xml.ReadSubtree();
          
                  car.ReadFrom(sub);
          
                  sub.Close();
          
                  return car;
              }
          }
          

          如果子类型的 XML 有任何子元素严格附加在超类型的内容之后或之前,则此方法可以正常工作。否则你必须做更多的工作来重用超级类型的序列化,将它分解成更小的方法(例如,基础有加载车轮数量、门、发动机尺寸的方法;赛车调用LoadDoorDataLoadAeroFoilData , LoadWheelData 如果赛车的 XML 在车门和车轮数据之间有翼型数据。对于没有施加逻辑顺序的格式(XMI、RDF),您必须检查本地名称来决定调用哪个专用方法,如果你想将它与虚拟方法结合起来会有点混乱。在这种情况下,最好使用单独的序列化助手。

          如果要创建的类型集不固定为几个类型,则可以在工厂中使用其他机制。

          【讨论】:

            猜你喜欢
            • 2023-04-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多