简体   繁体   中英

XPath matches for a XML serialized DataContract class

I have a DataContract class MyDataContract . I am serializing it to a XML file. Later, at a totally different "place" in my application I am loading this file. There I want to verify to load only, if the content of the file matches special conditions. Let's say I store a person and the association to the person's vehicle assuming a person can own just one vehicle. :-)

Here the DataContract classes:

namespace Test.DataContracts
{
  [DataContract]
  public class MyDataContract
  {
    [DataMember]
    public string Identifier { get; set; }

    [DataMember]
    public Common.Person Person { get; set; }

    [DataMember]
    public Common.Vehicle Vehicle { get; set; }
  }
}

namespace Test.DataContracts.Common
{
  [DataContract]
  public class Person
  {
    [DataMember]
    public Global.Gender Gender { get; set; }

    [DataMember]
    public string Info { get; set; }

    [DataMember]
    public string Name { get; set; }
  }

  [DataContract]
  public class Vehicle
  {
    [DataMember]
    public string Info { get; set; }

    [DataMember]
    public string Name { get; set; }

    [DataMember]
    public string Vendor { get; set; }
  }
}

namespace Test.DataContracts.Global
{
  [DataContract]
  public class Gender
  {
    [DataMember]
    public int Type { get; set; }

    [DataMember]
    public string Name { get; set; }
  }
}

Results in the following serialized XML:

<?xml version="1.0" encoding="utf-8"?>
<MyDataContract xmlns="http://schemas.datacontract.org/2004/07/Test.DataContracts" xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Identifier>123456789</Identifier>
  <Person xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
    <a:Gender xmlns:b="http://schemas.datacontract.org/2004/07/Test.DataContracts.Global">
      <b:Name>Female</b:Name>
      <b:Type>0</b:Type>
    </a:Gender>
    <a:Info>She is a beautiful lady.</a:Info>
    <a:Name>Jane Doe</a:Name>
  </Person>
  <Vehicle xmlns:a="http://schemas.datacontract.org/2004/07/Test.DataContracts.Common">
    <a:Info>A super great car.</a:Info>
    <a:Name>Mustang 1983 Turbo GT</a:Name>
    <a:Vendor>Ford</a:Vendor>
  </Vehicle>
</MyDataContract>

Now I want to filter out only Female (Type = 0) persons that own any Ford (Vendor = Ford) vehicle. I tried the following, but it always results in false for the matches.

string path = @"c:\janedoe.xml";

var xmlDoc = new XmlDocument();
xmlDoc.Load(path);
XmlNodeList xNodes = xmlDoc.SelectNodes(@"//namespace::*[not(. = ../../namespace::*)]");
var xNamespaceManager = new XmlNamespaceManager(xmlDoc.NameTable);

foreach (XmlNode node in xNodes)
{
  if (!string.IsNullOrWhiteSpace(xNamespaceManager.LookupNamespace(node.LocalName))) continue;
  xNamespaceManager.AddNamespace(node.LocalName, node.Value);
}

using (var fs = new FileStream(path, FileMode.Open))
{
  var xDocument = new XPathDocument(fs);
  var xNavigator = xDocument.CreateNavigator();
  XPathExpression exp1 = xNavigator.Compile(string.Format("MyDataContract/Person/Gender/Type/descendant::*[contains(text(), '{0}')]", "0"));
  exp1.SetContext(xNamespaceManager);
  XPathExpression exp2 = xNavigator.Compile(string.Format("MyDataContract/Vehicle/Vendor/descendant::*[contains(text(), '{0}')]", "Ford"));
  exp2.SetContext(xNamespaceManager);

  if (xNavigator.Matches(exp1) && xNavigator.Matches(exp2))
  {
    Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
  }
  else
  {
    Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
  }
}

Can anyone help?

UPDATE 1 - I have changed the code using XmlNamespaceManager. But still results in false when executing xNavigator.Matches(exp1) .

If you have XDocument it is easier to use LINQ-to-XML:

var xdoc = XDocument.Load(memorystream);
// Making it simple, grab the first
var type = xdoc.Descendants(XName.Get("Type","http://schemas.datacontract.org/2004/07/Test.DataContracts.Global")).FirstOrDefault();
var vendor = xdoc.Descendants(XName.Get("Vendor", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Common")).FirstOrDefault();
string path = "blah";
if (type != null && type.Value == "0" && vendor != null && vendor.Value == "Ford")
{
    Console.WriteLine("File '{0}' indicates a female person that owns a vehicle of Ford.", path);
}
else
{
    Console.WriteLine("File '{0}' has no matches (female and Ford).", path);
}

If you are sure that XPath is the only solution you need:

using System.Xml.XPath;

var document = XDocument.Load(fileName);
var namespaceManager = new XmlNamespaceManager(new NameTable());
namespaceManager.AddNamespace("a", "http://schemas.datacontract.org/2004/07/Test.DataContracts.Global");
var name = document.XPathSelectElement("path", namespaceManager).Value;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM