好的,我对此很感兴趣,所以我一起破解了一些代码。它并不漂亮,仅真正支持这个用例,但我认为它可以完成您正在寻找的工作,并且可以作为一个不错的平台开始。我也没有对它进行过彻底的测试。最后,您需要修改代码以使其返回内容(请参阅名为 Output() 的方法)。
这是代码:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
namespace XPathInCE
{
class Program
{
static void Main(string[] args)
{
try
{
if (args.Length != 2)
{
ShowUsage();
}
else
{
Extract(args[0], args[1]);
}
}
catch (Exception ex)
{
Console.WriteLine("{0} was thrown", ex.GetType());
Console.WriteLine(ex.Message);
Console.WriteLine(ex.StackTrace);
}
Console.WriteLine("Press ENTER to exit");
Console.ReadLine();
}
private static void Extract(string filePath, string queryString)
{
if (!File.Exists(filePath))
{
Console.WriteLine("File not found! Path: {0}", filePath);
return;
}
XmlReaderSettings settings = new XmlReaderSettings { IgnoreComments = true, IgnoreWhitespace = true };
using (XmlReader reader = XmlReader.Create(filePath, settings))
{
XPathQuery query = new XPathQuery(queryString);
query.Find(reader);
}
}
static void ShowUsage()
{
Console.WriteLine("No file specified or incorrect number of parameters");
Console.WriteLine("Args must be: Filename XPath");
Console.WriteLine();
Console.WriteLine("Sample usage:");
Console.WriteLine("XPathInCE someXmlFile.xml ConfigurationRelease/Profiles/Profile[Name='MyProfileName']/Screens/Screen[Id='MyScreenId']/Settings/Setting[Name='MySettingName']");
}
class XPathQuery
{
private readonly LinkedList<ElementOfInterest> list = new LinkedList<ElementOfInterest>();
private LinkedListNode<ElementOfInterest> currentNode;
internal XPathQuery(string query)
{
Parse(query);
currentNode = list.First;
}
internal void Find(XmlReader reader)
{
bool skip = false;
while (true)
{
if (skip)
{
reader.Skip();
skip = false;
}
else
{
if (!reader.Read())
{
break;
}
}
if (reader.NodeType == XmlNodeType.EndElement
&& String.Compare(reader.Name, currentNode.Previous.Value.ElementName, StringComparison.CurrentCultureIgnoreCase) == 0)
{
currentNode = currentNode.Previous ?? currentNode;
continue;
}
if (reader.NodeType == XmlNodeType.Element)
{
string currentElementName = reader.Name;
Console.WriteLine("Considering element: {0}", reader.Name);
if (String.Compare(reader.Name, currentNode.Value.ElementName, StringComparison.CurrentCultureIgnoreCase) != 0)
{
// don't want
Console.WriteLine("Skipping");
skip = true;
continue;
}
if (!FindAttributes(reader))
{
// don't want
Console.WriteLine("Skipping");
skip = true;
continue;
}
// is there more?
if (currentNode.Next != null)
{
currentNode = currentNode.Next;
continue;
}
// we're at the end, this is a match! :D
Console.WriteLine("XPath match found!");
Output(reader, currentElementName);
}
}
}
private bool FindAttributes(XmlReader reader)
{
foreach (AttributeOfInterest attributeOfInterest in currentNode.Value.Attributes)
{
if (String.Compare(reader.GetAttribute(attributeOfInterest.Name), attributeOfInterest.Value,
StringComparison.CurrentCultureIgnoreCase) != 0)
{
return false;
}
}
return true;
}
private static void Output(XmlReader reader, string name)
{
while (reader.Read())
{
// break condition
if (reader.NodeType == XmlNodeType.EndElement
&& String.Compare(reader.Name, name, StringComparison.CurrentCultureIgnoreCase) == 0)
{
return;
}
if (reader.NodeType == XmlNodeType.Element)
{
Console.WriteLine("Element {0}", reader.Name);
Console.WriteLine("Attributes");
for (int i = 0; i < reader.AttributeCount; i++)
{
reader.MoveToAttribute(i);
Console.WriteLine("Attribute: {0} Value: {1}", reader.Name, reader.Value);
}
}
if (reader.NodeType == XmlNodeType.Text)
{
Console.WriteLine("Element value: {0}", reader.Value);
}
}
}
private void Parse(string query)
{
IList<string> elements = query.Split('/');
foreach (string element in elements)
{
ElementOfInterest interestingElement = null;
string elementName = element;
int attributeQueryStartIndex = element.IndexOf('[');
if (attributeQueryStartIndex != -1)
{
int attributeQueryEndIndex = element.IndexOf(']');
if (attributeQueryEndIndex == -1)
{
throw new ArgumentException(String.Format("Argument: {0} has a [ without a corresponding ]", query));
}
elementName = elementName.Substring(0, attributeQueryStartIndex);
string attributeQuery = element.Substring(attributeQueryStartIndex + 1,
(attributeQueryEndIndex - attributeQueryStartIndex) - 2);
string[] keyValPair = attributeQuery.Split('=');
if (keyValPair.Length != 2)
{
throw new ArgumentException(String.Format("Argument: {0} has an attribute query that either has too many or insufficient = marks. We currently only support one", query));
}
interestingElement = new ElementOfInterest(elementName);
interestingElement.Add(new AttributeOfInterest(keyValPair[0].Trim().Replace("'", ""),
keyValPair[1].Trim().Replace("'", "")));
}
else
{
interestingElement = new ElementOfInterest(elementName);
}
list.AddLast(interestingElement);
}
}
class ElementOfInterest
{
private readonly string elementName;
private readonly List<AttributeOfInterest> attributes = new List<AttributeOfInterest>();
public ElementOfInterest(string elementName)
{
this.elementName = elementName;
}
public string ElementName
{
get { return elementName; }
}
public List<AttributeOfInterest> Attributes
{
get { return attributes; }
}
public void Add(AttributeOfInterest attribute)
{
Attributes.Add(attribute);
}
}
class AttributeOfInterest
{
private readonly string name;
private readonly string value;
public AttributeOfInterest(string name, string value)
{
this.name = name;
this.value = value;
}
public string Value
{
get { return value; }
}
public string Name
{
get { return name; }
}
}
}
}
}
这是我使用的测试输入:
<?xml version="1.0" encoding="utf-8" ?>
<ConfigurationRelease>
<Profiles>
<Profile Name ="MyProfileName">
<Screens>
<Screen Id="MyScreenId">
<Settings>
<Setting Name="MySettingName">
<Paydirt>Good stuff</Paydirt>
</Setting>
</Settings>
</Screen>
</Screens>
</Profile>
<Profile Name ="SomeProfile">
<Screens>
<Screen Id="MyScreenId">
<Settings>
<Setting Name="Boring">
<Paydirt>NOES you should not find this!!!</Paydirt>
</Setting>
</Settings>
</Screen>
</Screens>
</Profile>
<Profile Name ="SomeProfile">
<Screens>
<Screen Id="Boring">
<Settings>
<Setting Name="MySettingName">
<Paydirt>NOES you should not find this!!!</Paydirt>
</Setting>
</Settings>
</Screen>
</Screens>
</Profile>
<Profile Name ="Boring">
<Screens>
<Screen Id="MyScreenId">
<Settings>
<Setting Name="MySettingName">
<Paydirt>NOES you should not find this!!!</Paydirt>
</Setting>
</Settings>
</Screen>
</Screens>
</Profile>
</Profiles>
</ConfigurationRelease>
这是我得到的输出。
C:\Sandbox\XPathInCE\XPathInCE\bin\Debug>XPathInCE MyXmlFile.xml ConfigurationRe
lease/Profiles/Profile[Name='MyProfileName']/Screens/Screen[Id='MyScreenId']/Set
tings/Setting[Name='MySettingName']
Considering element: ConfigurationRelease
Considering element: Profiles
Considering element: Profile
Considering element: Screens
Considering element: Screen
Considering element: Settings
Considering element: Setting
XPath match found!
Element Paydirt
Attributes
Element value: Good stuff
Considering element: Profile
Skipping
Considering element: Profile
Skipping
Considering element: Profile
Skipping
Press ENTER to exit
我在桌面上运行它,但它是我生成的 CF 2.00 .exe,因此它应该可以在 CE 上正常工作。如您所见,当它不匹配时它会跳过,因此它不会遍历整个文件。
感谢任何人的反馈,特别是如果人们有使代码更简洁的指针。