방법: LINQ to XML 축 메서드 작성
업데이트: November 2007
XML 트리에서 컬렉션을 검색하는 축 메서드를 작성할 수 있습니다. 축 메서드를 작성하는 가장 좋은 방법 중 하나는 요소나 특성의 컬렉션을 반환하는 확장 메서드를 작성하는 것입니다. 응용 프로그램의 요구 사항에 따라 요소나 특성의 특정 하위 집합을 반환하는 확장 메서드를 작성할 수 있습니다.
예제
다음 예제에서는 두 가지 확장 메서드를 사용합니다. 첫 번째 확장 메서드인 GetXPath는 XObject에 대해 작동하며 계산될 때 노드나 특성을 반환할 XPath 식을 반환합니다. 두 번째 확장 메서드인 Find는 XElement에 대해 작동하며 지정된 일부 텍스트가 포함된 XElement 개체와 XAttribute 개체의 컬렉션을 반환합니다.
이 예제에서는 XML 문서로 샘플 XML 파일: 여러 구매 주문(LINQ to 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