存储和回放XML节点的最佳方法是什么?

时间:2013-02-14 18:22:54

标签: c# .net xml memory-management xmlreader

我有一些看起来像这样的XML(高度简化):

<?xml version="1.0"?>
<example>
    <shortcuts>
        <shortcut name="shortcut1">
            <property name="name1" value="value1" />
            <property name="name2" value="value2" />
        </shortcut>
    </shortcuts>
    <data>
        <datum name="datum1">
            <property name="name1" value="value1" />
            <property name="name2" value="value2" />    
        </datum>
        <datum name="datum2">
            <shortcutRef name="shortcut1" />
        </datum>
        <datum name="datum3">
            <shortcutRef name="shortcut1" />
            <property name="name3" value="value3" />    
        </datum>
    </data>
</example>

如您所见,它的结构使得可以定义由一个或多个属性组成的“快捷方式”。然后可以使用属性或一个或多个快捷方式或两者的混合(并且没有特定顺序)明确描述数据。

我想用XmlReader解析它(XmlDocument会更容易,但因为XML文件太大而无法在这里工作)。我认为这样做的一个好方法是将每个快捷方式的XML子树存储在由快捷方式名称键入的字典中,这是唯一的。然后,当它们被引用时,我可以通读该子树XmlReader而不是主要的。但是,子树XmlReader仍然必须链接到主XmlReader,因为出来的XML不是我所期望的。这是我的代码:

using(XmlReader xml = XmlReader.Create("example.xml"))
{
    Dictionary<string, XmlReader> shortcuts = new Dictionary<string, XmlReader>();
    xml.ReadToDescendant("shortcuts");
    xml.ReadToDescendant("shortcut");
    do
    {
        shortcuts.Add(xml.GetAttribute("name"), xml.ReadSubtree());
    } while(xml.ReadToNextSibling("shortcut"));

    xml.ReadToFollowing("data");
    while(xml.ReadToFollowing("datum"))
    {
        Console.WriteLine(xml.GetAttribute("name"));

        XmlReader datum = xml.ReadSubtree();
        while(datum.Read())
        {
            if(datum.Name == "property")
            {
                Console.WriteLine(datum.GetAttribute("name") + ':' + datum.GetAttribute("value"));
            }
            else if(datum.Name == "shortcutRef")
            {
                XmlReader shortcut_ref = shortcuts[datum.GetAttribute("name")];
                while(shortcut_ref.ReadToFollowing("property"))
                {
                    Console.WriteLine(shortcut_ref.GetAttribute("name") + ':' + shortcut_ref.GetAttribute("value"));
                }
            }
        }
    }
}

解析以这种方式构造的XML的最佳方法是什么?

4 个答案:

答案 0 :(得分:1)

您可以使用LinqToXml,如Mathieson建议的那样。以下是使用查找的示例。

XElement root = XElement.Load(file); // or .Parse(string)
var shortcuts = root.Descendants("shortcut").SelectMany(s =>
    s.Elements("property").ToLookup(
        k => k.Parent.Attribute("name").Value,
        v => v.Select(p => new
        {
            Name = p.Attribute("name").Value,
            Value = p.Attribute("value").Value
        })));

这将导致类似字典的结构,但查找具有多个键值。因此,您将通过快捷方式名称查找所有属性。

答案 1 :(得分:0)

目前还不完全清楚你想做什么 - 但是因为你使用“回放”这个词,我猜你不需要将XML节点(数据/数据)中的所有值都存储在内存中(你可以在使用后丢弃它们,但是你需要缓存快捷方式属性,以便在引用它们时可以重新遍历它们......你只是拥有它,而不是存储XML节点,只需将对象存储在字典。

public class Property
{
    public string Name { get; set; }
    public string Value { get; set; }
}

public class Shortcut
{
    public List<Property> Properties = new List<Property>();
}

class Program
{
    static void Main(string[] args)
    {
        FileStream fs = new FileStream(@"c:\temp\example.xml", FileMode.Open, FileAccess.Read);
        XmlTextReader reader = new XmlTextReader(fs);

        Dictionary<string, Shortcut> ShortcutDictionary = new Dictionary<string, Shortcut>();

        while (reader.Read())
        {
            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "shortcuts")
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "shortcut")
                    {
                        Shortcut shortcut = new Shortcut();
                        ShortcutDictionary.Add(reader.GetAttribute("name"), shortcut);
                        while (reader.Read())
                        {
                            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "property")
                                shortcut.Properties.Add(new Property() { Name = reader.GetAttribute("name"), Value = reader.GetAttribute("value") });
                            else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "shortcut")
                                break;
                        }
                    }
                    else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "shortcuts")
                        break;
                }
            }

            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "data")
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "datum")
                    {
                        while (reader.Read())
                        {
                            if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "property")
                            {
                                Console.WriteLine(reader.GetAttribute("name") + ':' + reader.GetAttribute("value"));
                            }
                            else if (reader.NodeType == XmlNodeType.Element && reader.LocalName == "shortcutRef")
                            {
                                foreach (Property property in ShortcutDictionary[reader.GetAttribute("name")].Properties)
                                    Console.WriteLine(property.Name + ':' + property.Value);
                            }
                            else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "datum")
                                break;
                        }
                    }
                    else if (reader.NodeType == XmlNodeType.EndElement && reader.LocalName == "data")
                        break;
                }
            }
        }

        reader.Close();
        fs.Close();
    }
}

否则,如果不是,那么您尝试以随机访问方式访问串行数据。您最好的选择是将数据转换/保存到数据库中。像SQLite这样的东西就可以做到。

答案 2 :(得分:-1)

我使用xml序列化。只需创建一个具有所需结构的POCO类(在本例中,是一个带有“data”和“shortcuts”列表的“exemple”类),注释一些成员,以便它们可以呈现为属性并调用xml serialize。看看这个:

如何创建课程:http://www.codeproject.com/Articles/483055/XML-Serialization-and-Deserialization-Part-1

如何序列化和反序列化:http://www.codeproject.com/Articles/347758/XML-Serialization-and-Deserialization

答案 3 :(得分:-2)

一般来说,解析XML文件可以完成两种主要的理解方式:
 1. DOM (文档对象模型)
 2. SAX (XML的简单API)

差异
DOM解析器从输入文档在内存中创建树结构,然后等待来自客户端的请求。但是SAX解析器不会创建任何内部结构。相反,它将输入文档的组件的出现作为事件,并告诉客户端在读取输入文档时读取的内容。
无论客户端实际需要多少,DOM解析器始终为客户端应用程序提供整个文档,但SAX解析器在任何给定时间始终只为文档的各个部分提供客户端应用程序。 使用DOM解析器,客户端应用程序中的方法调用必须是显式的,并形成一种链。但是使用SAX时,某些某些方法(通常由客户覆盖)将以某种特定事件发生时称为“回调”的方式自动(隐式)调用。
这些方法不必由客户端明确调用,但我们可以明确地调用它们。

DOM
1.节点树
2.内存:占用更多内存,优先用于小型XML文档 3.运行时较慢
4.存储为对象
5.编程方便 6.易于导航
SAX
1.事件顺序
2.不使用大型文档首选的任何内存 3.运行时更快
4.要创建对象
5.需要编写用于创建对象的代码 6.无法向后导航,因为它按顺序处理文档

实施.Net框架
XmlReader (某些方法)和 XmlDocuments 是基于DOM模型MSDN refrence构建的。
我还没有找到适用于SAX的官方.Net框架API,但您可以使用Towards a Declarative SAX FrameworkSax for .Net