1

我有一些看起来像这样的 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

4 回答 4

1

您可以按照 Mathieson 的建议使用 LinqToXml。这是使用查找的示例。

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
        })));

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

于 2013-02-14T20:04:23.110 回答
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 这样的东西会做到这一点。

于 2013-02-18T07:08:41.877 回答
-1

我会使用xml序列化。只需创建一个具有所需结构的 POCO 类(在这种情况下,是一个带有“数据”和“快捷方式”列表的“示例”类),注释一些成员,以便它们可以呈现为属性并调用 xml 序列化。看一下这个:

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

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

于 2013-02-18T14:37:08.747 回答
-2

通常解析 XML 文件可以通过两种主要的理智方式完成:
1. DOM(文档对象模型)
2. SAX(XML 的简单 API)

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

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

实现 .Net 框架:(以
XmlReader某种方式)并且XmlDocuments基于 DOM 模型、MSDN 参考构建。
我还没有找到 SAX 的官方 .Net 框架 API,但您可以使用Towards a Declarative SAX FrameworkSax for .Net

于 2013-02-19T07:14:21.487 回答