How to use the special "xml" namespace prefix with XElement - c#

I'm using XElement to build an XML document in C# and I'm trying to set
<myEelment xml:space="preserve">
Here's my current attempt:
myElement.SetAttributeValue(XName.Get("space", "xml"), "preserve");
but it comes out like this:
<myEelment p4:space="preserve" xmlns:p4="xml">
I understand how this is going wrong - my code is using "xml" as a namespace URI when I want to use as a namespace prefix. My problem is that AFAICT the "xml" namespace prefix is somehow implicit and doesn't actually have a namespace URI associated with it. So how can I generate attributes with the namespace prefix "xml"?

Standard namespaces are available as properties on the XNamespace class. Use that.
var myElement = doc.Descendants("myElement").Single();
myElement.SetAttributeValue(XNamespace.Xml + "space", "preserve");

Related

Get XML value via XPath [duplicate]

How does XPath deal with XML namespaces?
If I use
/IntuitResponse/QueryResponse/Bill/Id
to parse the XML document below I get 0 nodes back.
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<IntuitResponse xmlns="http://schema.intuit.com/finance/v3"
time="2016-10-14T10:48:39.109-07:00">
<QueryResponse startPosition="1" maxResults="79" totalCount="79">
<Bill domain="QBO" sparse="false">
<Id>=1</Id>
</Bill>
</QueryResponse>
</IntuitResponse>
However, I'm not specifying the namespace in the XPath (i.e. http://schema.intuit.com/finance/v3 is not a prefix of each token of the path). How can XPath know which Id I want if I don't tell it explicitly? I suppose in this case (since there is only one namespace) XPath could get away with ignoring the xmlns entirely. But if there are multiple namespaces, things could get ugly.
XPath 1.0/2.0
Defining namespaces in XPath (recommended)
XPath itself doesn't have a way to bind a namespace prefix with a namespace. Such facilities are provided by the hosting library.
It is recommended that you use those facilities and define namespace prefixes that can then be used to qualify XML element and attribute names as necessary.
Here are some of the various mechanisms which XPath hosts provide for specifying namespace prefix bindings to namespace URIs.
(OP's original XPath, /IntuitResponse/QueryResponse/Bill/Id, has been elided to /IntuitResponse/QueryResponse.)
C#:
XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
XmlNodeList nodes = el.SelectNodes(#"/i:IntuitResponse/i:QueryResponse", nsmgr);
Google Docs:
Unfortunately, IMPORTXML() does not provide a namespace prefix binding mechanism. See next section, Defeating namespaces in XPath, for how to use local-name() as a work-around.
Java (SAX):
NamespaceSupport support = new NamespaceSupport();
support.pushContext();
support.declarePrefix("i", "http://schema.intuit.com/finance/v3");
Java (XPath):
xpath.setNamespaceContext(new NamespaceContext() {
public String getNamespaceURI(String prefix) {
switch (prefix) {
case "i": return "http://schema.intuit.com/finance/v3";
// ...
}
});
Remember to call
DocumentBuilderFactory.setNamespaceAware(true).
See also:
Java XPath: Queries with default namespace xmlns
JavaScript:
See Implementing a User Defined Namespace Resolver:
function nsResolver(prefix) {
var ns = {
'i' : 'http://schema.intuit.com/finance/v3'
};
return ns[prefix] || null;
}
document.evaluate( '/i:IntuitResponse/i:QueryResponse',
document, nsResolver, XPathResult.ANY_TYPE,
null );
Note that if the default namespace has an associated namespace prefix defined, using the nsResolver() returned by Document.createNSResolver() can obviate the need for a customer nsResolver().
Perl (LibXML):
my $xc = XML::LibXML::XPathContext->new($doc);
$xc->registerNs('i', 'http://schema.intuit.com/finance/v3');
my #nodes = $xc->findnodes('/i:IntuitResponse/i:QueryResponse');
Python (lxml):
from lxml import etree
f = StringIO('<IntuitResponse>...</IntuitResponse>')
doc = etree.parse(f)
r = doc.xpath('/i:IntuitResponse/i:QueryResponse',
namespaces={'i':'http://schema.intuit.com/finance/v3'})
Python (ElementTree):
namespaces = {'i': 'http://schema.intuit.com/finance/v3'}
root.findall('/i:IntuitResponse/i:QueryResponse', namespaces)
Python (Scrapy):
response.selector.register_namespace('i', 'http://schema.intuit.com/finance/v3')
response.xpath('/i:IntuitResponse/i:QueryResponse').getall()
PhP:
Adapted from #Tomalak's answer using DOMDocument:
$result = new DOMDocument();
$result->loadXML($xml);
$xpath = new DOMXpath($result);
$xpath->registerNamespace("i", "http://schema.intuit.com/finance/v3");
$result = $xpath->query("/i:IntuitResponse/i:QueryResponse");
See also #IMSoP's canonical Q/A on PHP SimpleXML namespaces.
Ruby (Nokogiri):
puts doc.xpath('/i:IntuitResponse/i:QueryResponse',
'i' => "http://schema.intuit.com/finance/v3")
Note that Nokogiri supports removal of namespaces,
doc.remove_namespaces!
but see the below warnings discouraging the defeating of XML namespaces.
VBA:
xmlNS = "xmlns:i='http://schema.intuit.com/finance/v3'"
doc.setProperty "SelectionNamespaces", xmlNS
Set queryResponseElement =doc.SelectSingleNode("/i:IntuitResponse/i:QueryResponse")
VB.NET:
xmlDoc = New XmlDocument()
xmlDoc.Load("file.xml")
nsmgr = New XmlNamespaceManager(New XmlNameTable())
nsmgr.AddNamespace("i", "http://schema.intuit.com/finance/v3");
nodes = xmlDoc.DocumentElement.SelectNodes("/i:IntuitResponse/i:QueryResponse",
nsmgr)
SoapUI (doc):
declare namespace i='http://schema.intuit.com/finance/v3';
/i:IntuitResponse/i:QueryResponse
xmlstarlet:
-N i="http://schema.intuit.com/finance/v3"
XSLT:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:i="http://schema.intuit.com/finance/v3">
...
Once you've declared a namespace prefix, your XPath can be written to use it:
/i:IntuitResponse/i:QueryResponse
Defeating namespaces in XPath (not recommended)
An alternative is to write predicates that test against local-name():
/*[local-name()='IntuitResponse']/*[local-name()='QueryResponse']
Or, in XPath 2.0:
/*:IntuitResponse/*:QueryResponse
Skirting namespaces in this manner works but is not recommended because it
Under-specifies the full element/attribute name.
Fails to differentiate between element/attribute names in different
namespaces (the very purpose of namespaces). Note that this concern could be addressed by adding an additional predicate to check the namespace URI explicitly:
/*[ namespace-uri()='http://schema.intuit.com/finance/v3'
and local-name()='IntuitResponse']
/*[ namespace-uri()='http://schema.intuit.com/finance/v3'
and local-name()='QueryResponse']
Thanks to Daniel Haley for the namespace-uri() note.
Is excessively verbose.
XPath 3.0/3.1
Libraries and tools that support modern XPath 3.0/3.1 allow the specification of a namespace URI directly in an XPath expression:
/Q{http://schema.intuit.com/finance/v3}IntuitResponse/Q{http://schema.intuit.com/finance/v3}QueryResponse
While Q{http://schema.intuit.com/finance/v3} is much more verbose than using an XML namespace prefix, it has the advantage of being independent of the namespace prefix binding mechanism of the hosting library. The Q{} notation is known as Clark Notation after its originator, James Clark. The W3C XPath 3.1 EBNF grammar calls it a BracedURILiteral.
Thanks to Michael Kay for the suggestion to cover XPath 3.0/3.1's BracedURILiteral.
I use /*[name()='...'] in a google sheet to fetch some counts from Wikidata. I have a table like this
thes WD prop links items
NOM P7749 3925 3789
AAT P1014 21157 20224
and the formulas in cols links and items are
=IMPORTXML("https://query.wikidata.org/sparql?query=SELECT(COUNT(*)as?c){?item wdt:"&$B14&"[]}","//*[name()='literal']")
=IMPORTXML("https://query.wikidata.org/sparql?query=SELECT(COUNT(distinct?item)as?c){?item wdt:"&$B14&"[]}","//*[name()='literal']")
respectively. The SPARQL query happens not to have any spaces...
I saw name() used instead of local-name() in Xml Namespace breaking my xpath!, and for some reason //*:literal doesn't work.

XPath not working

I am trying to read the entitysets within the EDMX file from Entity Framework.
The EDMX file (XML format) has the following layout:
<edmx:Edmx Version="3.0" xmlns:edmx="http://schemas.microsoft.com/ado/2009/11/edmx">
<edmx:Runtime>
<edmx:ConceptualModels>
<Schema Namespace="Model" Alias="Self" p1:UseStrongSpatialTypes="false" xmlns:annotation="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns:p1="http://schemas.microsoft.com/ado/2009/02/edm/annotation" xmlns="http://schemas.microsoft.com/ado/2009/11/edm">
<EntityContainer Name="EntityModel" p1:LazyLoadingEnabled="true">
<EntitySet Name="TableName" EntityType="Model.TableName" />
I am using following XPath to get all EntitySet Nodes within the EntityContainer:
/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/Schema/EntityContainer/EntitySet
but I am getting no result with this C# code:
XmlDocument xdoc = new XmlDocument("pathtoedmx");
var ns = new XmlNamespaceManager(xdoc.NameTable);
ns.AddNamespace("edmx", "http://schemas.microsoft.com/ado/2009/11/edmx");
ns.AddNamespace("annotation", "http://schemas.microsoft.com/ado/2009/02/edm/annotation");
ns.AddNamespace("p1", "http://schemas.microsoft.com/ado/2009/02/edm/annotation");
ns.AddNamespace("", "http://schemas.microsoft.com/ado/2009/11/edm");
var entitySets = xdoc.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/Schema/EntityContainer/EntitySet", ns);
Already got the XPath from this tool (http://qutoric.com/xmlquire/), because I started not trusting my own XPath skills but it tells me the same XPath I was already using.
If I remove the "/Schema/EntityContainer/EntitySet" part its finding the "/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels", but not further on already tried to specify the "edmx" namespace ("edmx:/Schema") but no difference.
Hope you can help me out, already banging my head against the table. :)
Namespaces are a convention on how to combine two different XML dialects into a single document. Those prefixes really doesn't matter as long you keep your URI component exactly the same. For instance, take something like this:
ns.AddNamespace("xxx", "http://schemas.microsoft.com/ado/2009/11/edmx");
Console.WriteLine(xdoc.SelectNodes("/xxx:Edmx", ns).Count); // 1
You'll get one node because your namespace URI matched, despite your "wrong" namespace prefix.
If you have an attribute named xmlns, current element and it's children will inherits that namespace URI.
In your case, your root element doesn't have a default namespace and that's ok. But your Schemas element does have a namespace and you need to inform it. I came with this code:
// change "" to "edm"
ns.AddNamespace("edm", "http://schemas.microsoft.com/ado/2009/11/edm");
var entitySets = xdoc.SelectNodes("/edmx:Edmx/edmx:Runtime/edmx:ConceptualModels/edm:Schema/edm:EntityContainer/edm:EntitySet", ns);

Deleting xml:base attribute from an XDocument

I'm currently having trouble deleting a troublesome attribute from my Xdocument's root node: xml:base.
My Xdocument currentDoc:
<root xml:base="texthere">
<child/>
</root>
I looked at the documentation about xml:base here: http://www.w3.org/TR/xmlbase/.
I'm having trouble in my C# code to get rid of this because this xml: prefix doesn't have a declaration like other namespace prefixes.
This is what I have which isn't working:
currentDoc.Root.Attributes().Where(a => a.IsNamespaceDeclaration).Remove();
The "xml" namespace is defined:
currentDoc.Root.Attributes(XNamespace.Xml + "base").Remove();

namespace not loading correctly

I am reading in an xml namespace through a local file. When I am outputting I am getting
"
in the namespace. What am I doing wrong?
sample namespace looks like
xmlns="urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02"
xmlns:dacc="urn:rosettanet:specification:domain:Procurement:AccountClassification:xsd:codelist:01.03"
I am loading the namespace as follows:
string contents = File.ReadAllText("loadNamespaces.txt");
XmlNode ShipmentReceiptNotification0Node = xmlDoc.CreateElement("ShipmentReceiptNotification", contents);
In the CreateElement method, the second parameter is only the namespace. Just the "urn:rosettanet:specification:interchange:ShipmentReceiptNotification:xsd:schema:02.02" part.

How to write simple query to XElement?

Trying to use Linq to XML for the first time and having some problems. I have this XML file that needs to be read and used for various tasks. The file contains a list of entities called 'interfaces'. To start with I want to display a list of names of these interfaces.
Here is the XML file:
<?xml version="1.0" encoding="utf-8" ?>
<InterfaceList>
<Interface>
<InterfaceName>Account Lookup</InterfaceName>
<RequestXSD>ALREQ.xsd</RequestXSD>
<ResponseXSD>ALRES.xsd</ResponseXSD>
</Interface>
<Interface>
<InterfaceName>Balance Inquiry</InterfaceName>
<RequestXSD>BIREQ.xsd</RequestXSD>
<ResponseXSD>BIRES.xsd</ResponseXSD>
</Interface>
</InterfaceList>
Here is the query code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace Stub {
public class InterfaceList : XElement {
public void GetInterfaceNameList() {
var v = from interface in this.Elements("Interface")
select interface.Element("InterfaceName").Value;
}
}
}
The idea is to load InterfaceList from the file, and then to use it to query any I may need.
The problem is that I'm getting error message for everything in the query. here are a few of them:
Error 14 The name 'from' does not exist in the current context
Error 15 The type or namespace name 'select' could not be found (are
you missing a using directive or an assembly reference?)
Error
Error 16 'System.Xml.Linq.XElement.Value' is a 'property' but is used
like a 'type'
What's wrong here?
If you want to call your variable 'interface' (which is a reserved word) you will need to escape it, like this:
var v = from #interface in this.Elements("Interface")
select #interface.Element("InterfaceName").Value;
Probably better to just rename it though....

Categories

Resources