XmlNamespaceManager가 필요한 이유는 무엇입니까?
나는 적어도 .Net Framework에서는 XPath 쿼리를 수행 할 때 네임 스페이스 (또는 다소 어색하고 장황한 XPath 조건 자 / 함수 / 무엇이든) 을 처리하기 위해를 사용해야하는 이유에 대해 약간 건조 XmlNamespaceManager
해졌습니다. [local-name()=...
. 내가 할 네임 스페이스가 도움이 필요하거나 적어도하지만 이유를 이해 왜 그렇게 복잡?
간단한 XML 문서 (네임 스페이스 없음)를 쿼리하려면 ...
<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode>
<nodeName>Some Text Here</nodeName>
</rootNode>
... 같은 것을 사용할 수 있습니다 doc.SelectSingleNode("//nodeName")
(일치하는 것 <nodeName>Some Text Here</nodeName>
)
미스터리 # 1 : 내 첫 번째 성가심 -내가 올바르게 이해한다면-단순히 부모 / 루트 태그에 네임 스페이스 참조를 추가하는 것입니다 (하위 노드 태그의 일부로 사용되는지 여부에 관계없이).
<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns="http://someplace.org">
<nodeName>Some Text Here</nodeName>
</rootNode>
... 같은 결과를 얻으려면 몇 줄의 추가 코드가 필요합니다.
Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("ab", "http://s+omeplace.org")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//ab:nodeName", nsmgr)
... 본질적으로 접두사를 ab
사용하지 않는 노드를 찾기 위해 존재하지 않는 접두사 ( " ")를 꿈꾸는 것 입니다. 이것이 어떻게 말이 되는가? (개념적으로) 무엇이 잘못 doc.SelectSingleNode("//nodeName")
되었습니까?
미스터리 # 2 : 접두사를 사용하는 XML 문서가 있다고 가정 해 보겠습니다.
<?xml version="1.0" encoding="ISO-8859-1"?>
<rootNode xmlns:cde="http://someplace.org" xmlns:feg="http://otherplace.net">
<cde:nodeName>Some Text Here</cde:nodeName>
<feg:nodeName>Some Other Value</feg:nodeName>
<feg:otherName>Yet Another Value</feg:otherName>
</rootNode>
... 내가 올바르게 이해했다면 XmlNamespaceManager
단일 노드에 대한 쿼리를 작성하려면 두 네임 스페이스를에 추가해야 합니다.
Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("cde", "http://someplace.org")
nsmgr.AddNamespace("feg", "http://otherplace.net")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//feg:nodeName", nsmgr)
...이 경우 왜 (개념적으로) 네임 스페이스 관리자가 필요합니까?
** 아래 댓글로 편집 됨 **
편집 추가됨 : 수정되고 정제 된 질문은 내가 대부분의 경우라고 생각하는 XmlNamespaceManager의 명백한 중복성과 URI에 대한 접두사 매핑을 지정하는 네임 스페이스 관리자의 사용을 기반으로합니다.
네임 스페이스 접두사 ( "cde")와 네임 스페이스 URI ( "http://someplace.org")의 직접 매핑이 소스 문서에 명시 적으로 명시되어있는 경우 :
...<rootNode xmlns:cde="http://someplace.org"...
프로그래머가 쿼리를 만들기 전에 해당 매핑을 다시 만들어야하는 개념적 필요는 무엇입니까?
기본 요점 ( 위의 Kev가 지적 했듯이 )은 네임 스페이스 URI가 네임 스페이스 접두사가 아닌 네임 스페이스의 중요한 부분이며 접두사는 "임의의 편의"라는 것입니다.
문서를 사용하여 작동하는 마법이 아니라 네임 스페이스 관리자가 필요한 이유에 대해서는 두 가지 이유를 생각할 수 있습니다.
이유 1
예제에서와 같이 documentElement에 네임 스페이스 선언 만 추가하도록 허용 된 경우 selectSingleNode가 정의 된 모든 것을 사용하는 것은 실제로 사소한 일입니다.
그러나 문서의 모든 요소에 네임 스페이스 접두사를 정의 할 수 있으며 네임 스페이스 접두사는 문서의 지정된 네임 스페이스에 고유하게 바인딩되지 않습니다. 다음 예를 고려하십시오.
<w xmlns:a="mynamespace">
<a:x>
<y xmlns:a="myOthernamespace">
<z xmlns="mynamespace">
<b:z xmlns:b="mynamespace">
<z xmlns="myOthernamespace">
<b:z xmlns:b="myOthernamespace">
</y>
</a:x>
</w>
이 예에서는 무엇을 원하는 것 //z
, //a:z
그리고 //b:z
반환에? 어떤 종류의 외부 네임 스페이스 관리자없이 어떻게 표현 하시겠습니까?
이유 2
사용중인 네임 스페이스 접두사에 대해 알 필요없이 동등한 문서에 대해 동일한 XPath 표현식을 재사용 할 수 있습니다.
myXPathExpression = "//z:y"
doc1.selectSingleNode(myXPathExpression);
doc2.selectSingleNode(myXPathExpression);
doc1 :
<x>
<z:y xmlns:z="mynamespace" />
</x>
doc2 :
<x xmlns"mynamespace">
<y>
</x>
네임 스페이스 관리자없이이 후자의 목표를 달성하려면 각 문서를 검사하여 각 문서에 대한 사용자 지정 XPath 표현식을 작성해야합니다.
그 이유는 간단합니다. XPath 쿼리에서 사용하는 접두사와 xml 문서에서 선언 된 접두사 간에는 필수 연결이 없습니다. 예제를 제공하기 위해 다음 xml은 의미 상 동일합니다.
<aaa:root xmlns:aaa="http://someplace.org">
<aaa:element>text</aaa:element>
</aaa:root>
vs
<bbb:root xmlns:bbb="http://someplace.org">
<bbb:element>text</bbb:element>
</bbb:root>
" ccc:root/ccc:element
"쿼리는 네임 스페이스 관리자에 매핑이있는 경우 두 인스턴스와 일치합니다.
nsmgr.AddNamespace("ccc", "http://someplace.org")
.NET 구현은 쿼리 리터럴에 대해 정의 된 접두사가 있고 네임 스페이스 값이 문서의 실제 값과 일치하는 경우에만 xml에서 사용되는 리터럴 접두사에 대해 신경 쓰지 않습니다. 사용 된 문서간에 접두사가 다르고 일반적인 경우에 올바른 구현 인 경우에도 상수 쿼리 식을 가져야합니다.
내가 말할 수있는 한, 다음 과 같은 문서가있는 경우 접두사가 붙은 노드 XmlNamespaceManager
를 가져 오기 위해 수동으로 정의해야 할 이유가 없습니다 abc
.
<itemContainer xmlns:abc="http://abc.com" xmlns:def="http://def.com">
<abc:nodeA>...</abc:nodeA>
<def:nodeB>...</def:nodeB>
<abc:nodeC>...</abc:nodeC>
</itemContainer>
Microsoft xmlns:abc
는 부모 노드에 이미 지정된 것을 감지하기 위해 무언가를 작성하는 데 신경을 쓸 수 없었 습니다. 나는 틀릴 수 있으며, 그렇다면이 답변에 대한 의견을 환영하여 업데이트 할 수 있습니다.
However, this blog post seems to confirm my suspicion. It basically says that you need to manually define an XmlNamespaceManager
and manually iterate through the xmlns:
attributes, adding each one to the namespace manager. Dunno why Microsoft couldn't do this automatically.
Here's a method I created based on that blog post to automatically generate an XmlNamespaceManager
based on the xmlns:
attributes of a source XmlDocument
:
/// <summary>
/// Creates an XmlNamespaceManager based on a source XmlDocument's name table, and prepopulates its namespaces with any 'xmlns:' attributes of the root node.
/// </summary>
/// <param name="sourceDocument">The source XML document to create the XmlNamespaceManager for.</param>
/// <returns>The created XmlNamespaceManager.</returns>
private XmlNamespaceManager createNsMgrForDocument(XmlDocument sourceDocument)
{
XmlNamespaceManager nsMgr = new XmlNamespaceManager(sourceDocument.NameTable);
foreach (XmlAttribute attr in sourceDocument.SelectSingleNode("/*").Attributes)
{
if (attr.Prefix == "xmlns")
{
nsMgr.AddNamespace(attr.LocalName, attr.Value);
}
}
return nsMgr;
}
And I use it like so:
XPathNavigator xNav = xmlDoc.CreateNavigator();
XPathNodeIterator xIter = xNav.Select("//abc:NodeC", createNsMgrForDocument(xmlDoc));
I answer to point 1:
Setting a default namespace for an XML document still means that the nodes, even without a namespace prefix, i.e.:
<rootNode xmlns="http://someplace.org">
<nodeName>Some Text Here</nodeName>
</rootNode>
are no longer in the "empty" namespace. You still need some way to reference these nodes using XPath, so you create a prefix to reference them, even if it is "made up".
To answer point 2:
<rootNode xmlns:cde="http://someplace.org" xmlns:feg="http://otherplace.net">
<cde:nodeName>Some Text Here</cde:nodeName>
<feg:nodeName>Some Other Value</feg:nodeName>
<feg:otherName>Yet Another Value</feg:otherName>
</rootNode>
Internally in the instance document, the nodes that reside in a namespace are stored with their node name and their long namespace name, it's called (in W3C parlance) an expanded name.
For example <cde:nodeName>
is essentially stored as <http://someplace.org:nodeName>
. A namespace prefix is an arbitrary convenience for humans so that when we type out XML or have to read it we don't have to do this:
<rootNode>
<http://someplace.org:nodeName>Some Text Here</http://someplace.org:nodeName>
<http://otherplace.net:nodeName>Some Other Value</http://otherplace.net:nodeName>
<http://otherplace.net:otherName>Yet Another Value</http://otherplace.net:otherName>
</rootNode>
When an XML document is searched, it's not searched by the friendly prefix, they search is done by namespace URI so you have to tell XPath about your namespaces via a namespace table passed in using XmlNamespaceManager
.
You need to register the URI/prefix pairs to the XmlNamespaceManager instance to let SelectSingleNode() know which particular "nodeName" node you're referring to - the one from "http://someplace.org" or the one from "http://otherplace.net".
Please note that the concrete prefix name doesn't matter when you're doing the XPath query. I believe this works too:
Dim nsmgr As New XmlNamespaceManager(doc.NameTable)
nsmgr.AddNamespace("any", "http://someplace.org")
nsmgr.AddNamespace("thing", "http://otherplace.net")
Dim desiredNode As XmlNode = doc.SelectSingleNode("//thing:nodeName", nsmgr)
SelectSingleNode() just needs a connection between the prefix from your XPath expression and the namespace URI.
This thread has helped me understand the issue of namespaces much more clearly. Thanks. When I saw Jez's code, I tried it because it looked like a better solution than I had programmed. I discovered some shortcomings with it, though. As written, it looks only in the root node (but namespaces can be listed anywhere.), and it doesn't handle default namespaces. I tried to address these issues by modifying his code, but to no avail.
Here is my version of that function. It uses regular expressions to find the namespace mappings throughout the file; works with default namespaces, giving them the arbitrary prefix 'ns'; and handles multiple occurrences of the same namespace.
private XmlNamespaceManager CreateNamespaceManagerForDocument(XmlDocument document)
{
var nsMgr = new XmlNamespaceManager(document.NameTable);
// Find and remember each xmlns attribute, assigning the 'ns' prefix to default namespaces.
var nameSpaces = new Dictionary<string, string>();
foreach (Match match in new Regex(@"xmlns:?(.*?)=([\x22\x27])(.+?)\2").Matches(document.OuterXml))
nameSpaces[match.Groups[1].Value + ":" + match.Groups[3].Value] = match.Groups[1].Value == "" ? "ns" : match.Groups[1].Value;
// Go through the dictionary, and number non-unique prefixes before adding them to the namespace manager.
var prefixCounts = new Dictionary<string, int>();
foreach (var namespaceItem in nameSpaces)
{
var prefix = namespaceItem.Value;
var namespaceURI = namespaceItem.Key.Split(':')[1];
if (prefixCounts.ContainsKey(prefix))
prefixCounts[prefix]++;
else
prefixCounts[prefix] = 0;
nsMgr.AddNamespace(prefix + prefixCounts[prefix].ToString("#;;"), namespaceURI);
}
return nsMgr;
}
참고URL : https://stackoverflow.com/questions/7178111/why-is-xmlnamespacemanager-necessary
'Development Tip' 카테고리의 다른 글
Android에서 ARM EABI v7a 시스템 이미지를 사용하는 방법은 무엇입니까? (0) | 2020.11.15 |
---|---|
변환 생성자 대 변환 연산자 : 우선 순위 (0) | 2020.11.15 |
Laravel 5에서 모듈 식 앱을 구성하는 방법은 무엇입니까? (0) | 2020.11.15 |
자식 요소가 부모 스타일을 상속하지 못하도록하는 CSS (0) | 2020.11.15 |
Imagemagick을 사용하여 GIF, PNG 및 JPG를 .ICO 파일로 변환 (0) | 2020.11.15 |