65.9K
CodeProject 正在变化。 阅读更多。
Home

基于委托的 XML 处理器,用于从 XML 配置对象

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.38/5 (5投票s)

2006 年 8 月 20 日

CPOL

2分钟阅读

viewsIcon

30233

downloadIcon

90

这是一种灵活、内存高效的 XML 解析方法,无需处理 XmlReader 的复杂性

引言

有时我需要解析 XML 文件并配置一些对象。XmlReader 非常灵活,但对于手头的简单任务来说级别太低了。内置的序列化也是一个诱人的选择,但不够灵活,而且过于侵入。对于大型文件,DOM 的内存消耗太大。因此,经过多次的挫败和思考,我决定编写一个利用委托的简单处理器类。

注意:这是一个正在进行中的工作。它尚未处理命名空间,并且可能在几个方面得到改进。这只是一个起点,所以请随时向我发送改进或建议。

问题

假设您想从 XML 文件将数据导入到您的对象模型中,并将每个对象保存到数据库。除了从 XML 加载数据外,您还必须在每个对象上设置外键。

这是一个持久性对象的示例

public class Company
{
    public int CompanyId;
    public string CompanyName;
    
    public void Save() { ... }
}

public class Employee
{
    public int EmployeeId;
    public int ManagerEmployeeId;
    public int CompanyId;
    public string EmployeeName;
    
    public void Save() { ...  }
}

解决方案

我提出的最小 API 由两个类和一个委托组成

public class XmlProcessor
{
    public XmlProcessor(XmlTextReader rdr);
    public void RegisterElementHandler(string elementName, ElementHandler handler);      
    public void Start();            
}
    
public class Element
{
    public string Name; 
    public IDictionary Attributes = new Hashtable();
    public Element Parent;
    public object Peer;  //arbitrary object to associate with this element
        
    public Element FindAncestorByElementName(string name);
}
        
public delegate void ElementHandler(Element element);

为了使用 XmlProcessor 类,您所要做的就是注册一些元素处理程序,然后调用 Start() 方法,就像这样

XmlTextReader rdr = new XmlTextReader("../../SampleData.xml");
XmlProcessor processor = new XmlProcessor(rdr);
processor.RegisterElementHandler("Company", new ElementHandler(ImportCompany));
processor.RegisterElementHandler("Employee", new ElementHandler(ImportEmployee));
processor.Start();

用法详情

和往常一样,最简单的解释就是一个例子。:) 这是示例 XML,让您了解我们正在处理的内容

<LoadData>
        <Company CompanyName="Microsoft">
                <Employee Name="Steve Ballmer">
                        <Employee Name="Anders Hejlsberg">
                                <Employee Name="Joe Developer"/>
                                <Employee Name="Jane Developer"/>
                        </Employee>
                        <Employee Name="Random VP"/>
                </Employee>
        </Company>
</LoadData>

我们的对象模型对应于两个数据库表。 Employee 表有两个外键,一个指向 Company,另一个是自引用外键,其中包含经理的 ID。

首先,我们将为 Company 元素编写 ElementHandler。 这非常简单。 唯一看起来令人困惑的部分是设置元素对象的 Peer 属性。 这样做是为了子元素可以获取父 Company 对象的引用。 这是代码

private static void ImportCompany(Element element)
{
    Company c = new Company();
    c.CompanyName = (string) element.Attributes["CompanyName"];
    c.Save();
    element.Peer = c;
}

最后,我们将为 Employee 元素创建一个处理程序。 这将会有点棘手,但仍然很容易!

private static void ImportEmployee(Element element)
{
    //ensure Employee is nested in a Company Element
    Company c = (Company) element.FindAncestorByElementName("Company").Peer;
    if (c == null) 
        throw new Exception("Employee must belong to a Company");
    //create Employee and populate fields 
    Employee e = new Employee();
    e.EmployeeName = (string) element.Attributes["Name"];
    e.CompanyId = c.CompanyId;
    if (element.Parent.Name == "Employee")
    {
        Employee manager = (Employee) element.Parent.Peer;
        e.ManagerEmployeeId = manager.EmployeeId;
    }
    e.Save();
    element.Peer = e;
}

我们使用 element.FindAncestorByElementName() 来获取 EmployeeCompany。 为了获得 Employeemanager,首先我们检查 element.Parent.Name == "Employee"。 如果是,我们可以设置 ManagerEmployeeId。 同样,我们必须确保将 element.Peer 设置为当前的 Employee,以便任何子元素都可以访问其 managerEmployeeId

另请注意,对于任何我们不关心的元素(例如根 LoadData 元素),我们只需不注册处理程序即可。

结论

差不多就是这样! 您可以通过下载演示解决方案来获取 XmlProcessor 的实际实现(为了简洁起见)。 同样,XmlProcessor 的实际实现非常简单,可以改进,但它可以工作。

我希望这有帮助,并且您在使用它时玩得开心!

历史

  • 2006 年 8 月 20 日:首次发布
© . All rights reserved.