方法 : LINQ to XML 軸メソッドを記述する
更新 : November 2007
XML ツリーからコレクションを取得する独自の軸メソッドを記述できます。これを行うための最適な方法の 1 つは、要素または属性のコレクションを返す拡張メソッドを記述することです。アプリケーションの要件に基づいて、要素または属性の特定のサブセットを返す拡張メソッドを記述できます。
使用例
次の例では、2 つの拡張メソッドを使用します。最初の拡張メソッドである GetXPath は、XObject を処理して、評価時にノードまたは属性を返す XPath 式を返します。2 番目の拡張メソッドである Find は、XElement を処理して、指定されたテキストを含む XAttribute オブジェクトと XElement オブジェクトのコレクションを返します。
この例では、「サンプル XML ファイル : 複数の購買発注書 (LINQ to XML)」の XML ドキュメントを使用しています。
メモ : |
---|
次の例では、C# の yield return 構造を使用します。Visual Basic 2008 には類似した機能がないため、この例は C# のみを対象としています。 |
public static class MyExtensions
{
private static string GetQName(XElement xe)
{
string prefix = xe.GetPrefixOfNamespace(xe.Name.Namespace);
if (xe.Name.Namespace == XNamespace.None || prefix == null)
return xe.Name.LocalName.ToString();
else
return prefix + ":" + xe.Name.LocalName.ToString();
}
private static string GetQName(XAttribute xa)
{
string prefix =
xa.Parent.GetPrefixOfNamespace(xa.Name.Namespace);
if (xa.Name.Namespace == XNamespace.None || prefix == null)
return xa.Name.ToString();
else
return prefix + ":" + xa.Name.LocalName;
}
private static string NameWithPredicate(XElement el)
{
if (el.Parent != null && el.Parent.Elements(el.Name).Count() != 1)
return GetQName(el) + "[" +
(el.ElementsBeforeSelf(el.Name).Count() + 1) + "]";
else
return GetQName(el);
}
public static string StrCat<T>(this IEnumerable<T> source,
string separator)
{
return source.Aggregate(new StringBuilder(),
(sb, i) => sb
.Append(i.ToString())
.Append(separator),
s => s.ToString());
}
public static string GetXPath(this XObject xobj)
{
if (xobj.Parent == null)
{
XDocument doc = xobj as XDocument;
if (doc != null)
return ".";
XElement el = xobj as XElement;
if (el != null)
return "/" + NameWithPredicate(el);
// the XPath data model does not include white space text nodes
// that are children of a document, so this method returns null.
XText xt = xobj as XText;
if (xt != null)
return null;
XComment com = xobj as XComment;
if (com != null)
return
"/" +
(
com
.Document
.Nodes()
.OfType<XComment>()
.Count() != 1 ?
"comment()[" +
(com
.NodesBeforeSelf()
.OfType<XComment>()
.Count() + 1) +
"]" :
"comment()"
);
XProcessingInstruction pi = xobj as XProcessingInstruction;
if (pi != null)
return
"/" +
(
pi.Document.Nodes()
.OfType<XProcessingInstruction>()
.Count() != 1 ?
"processing-instruction()[" +
(pi
.NodesBeforeSelf()
.OfType<XProcessingInstruction>()
.Count() + 1) +
"]" :
"processing-instruction()"
);
return null;
}
else
{
XElement el = xobj as XElement;
if (el != null)
{
return
"/" +
el
.Ancestors()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
NameWithPredicate(el);
}
XAttribute at = xobj as XAttribute;
if (at != null)
return
"/" +
at
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
"@" + GetQName(at);
XComment com = xobj as XComment;
if (com != null)
return
"/" +
com
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
com
.Parent
.Nodes()
.OfType<XComment>()
.Count() != 1 ?
"comment()[" +
(com
.NodesBeforeSelf()
.OfType<XComment>()
.Count() + 1) + "]" :
"comment()"
);
XCData cd = xobj as XCData;
if (cd != null)
return
"/" +
cd
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
cd
.Parent
.Nodes()
.OfType<XText>()
.Count() != 1 ?
"text()[" +
(cd
.NodesBeforeSelf()
.OfType<XText>()
.Count() + 1) + "]" :
"text()"
);
XText tx = xobj as XText;
if (tx != null)
return
"/" +
tx
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
tx
.Parent
.Nodes()
.OfType<XText>()
.Count() != 1 ?
"text()[" +
(tx
.NodesBeforeSelf()
.OfType<XText>()
.Count() + 1) + "]" :
"text()"
);
XProcessingInstruction pi = xobj as XProcessingInstruction;
if (pi != null)
return
"/" +
pi
.Parent
.AncestorsAndSelf()
.InDocumentOrder()
.Select(e => NameWithPredicate(e))
.StrCat("/") +
(
pi
.Parent
.Nodes()
.OfType<XProcessingInstruction>()
.Count() != 1 ?
"processing-instruction()[" +
(pi
.NodesBeforeSelf()
.OfType<XProcessingInstruction>()
.Count() + 1) + "]" :
"processing-instruction()"
);
return null;
}
}
public static IEnumerable<XObject> Find(this XElement source, string value)
{
if (source.Attributes().Any())
{
foreach (XAttribute att in source.Attributes())
{
string contents = (string)att;
if (contents.Contains(value))
yield return att;
}
}
if (source.Elements().Any())
{
foreach (XElement child in source.Elements())
foreach (XObject s in child.Find(value))
yield return s;
}
else
{
string contents = (string)source;
if (contents.Contains(value))
yield return source;
}
}
}
class Program
{
static void Main(string[] args)
{
XElement purchaseOrders = XElement.Load("PurchaseOrders.xml");
IEnumerable<XObject> subset =
from xobj in purchaseOrders.Find("1999")
select xobj;
foreach (XObject obj in subset)
{
Console.WriteLine(obj.GetXPath());
if (obj.GetType() == typeof(XElement))
Console.WriteLine(((XElement)obj).Value);
else if (obj.GetType() == typeof(XAttribute))
Console.WriteLine(((XAttribute)obj).Value);
}
}
}
このコードを実行すると、次の出力が生成されます。
/PurchaseOrders/PurchaseOrder[1]/@OrderDate
1999-10-20
/PurchaseOrders/PurchaseOrder[1]/Items/Item[2]/ShipDate
1999-05-21
/PurchaseOrders/PurchaseOrder[2]/@OrderDate
1999-10-22
/PurchaseOrders/PurchaseOrder[3]/@OrderDate
1999-10-22