How to retrieve XML data using XPath Expression - asp.net

I have a XMLDataSource somewhat like:
<bookstore>
<author>author1</author>
<publication>publication1</publication>
<book>
<genre>Thriller</genre>
<name>ABC</name>
</book>
<book>
<genre>Romance</genre>
<name>XYZ</name>
</book>
<book>
<genre>Horror</genre>
<name>000</name>
</book>
</bookstore>
I am storing these in a asp:formview. I am able to store author and publication values but not sure how can I store the value of book/name based on some condition? Actually I just want to use condition that I need to store the value of "name" if "genere=Romance". something like this. I tried using XPath expression bookstore/book/genre[. ='Romance'] but not sure how to access the value of tag. Checked the following resource:
http://msdn.microsoft.com/en-us/library/ms256086.aspx
Appreciate if someone can help me.

I tried using XPath expression
bookstore/book/genre[. ='Romance']
but not sure how to access the value
of tag
Almost. This XPath expression:
/bookstore/book[genre='Romance']/name
String value: XYZ

You probably need to add /text() to get the contents of the XML tag instead of just the tag. There is a great XML cheat-sheet here that should help you.

Related

Saxon HE 9.7 XQuery results and existing document

I am new to Saxon.
In my java application, I have a requirement that I need to XQuery an existing dom4j document. The XQuery is to order few elements in an descending order by serialNo:
<?xml version="1.0" encoding="UTF-8"?>
<dataOfBooks:DataOfBooks xmlns:dataOfBooks="DataOfBooks">
<Id>ID123</Id>
<books>
<book>
<name>ccc</name>
<serialNo>77</serialNo>
</book>
<book>
<name>aaa</name>
<serialNo>99</serialNo>
</book>
</books>
</dataOfBooks:DataOfBooks>
Once I get the XQuery results, I need to add those back to the above existing document. I tried using net.sf.saxon.s9api. I was able to get the XQuery results back as below:
<?xml version="1.0" encoding="UTF-8"?>
<result:sequence
xmlns:result="http://saxon.sf.net/xquery-results"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<result:element>
<book xmlns:data="dataOfBooks">
<name>aaa</name>
<serialNo>99</serialNo>
</book>
<book xmlns:data="dataOfBooks">
<name>aaa</name>
<serialNo>77</serialNo>
</book>
</result:element>
</result:sequence>
But I have two issues. 1) the result has namespaces and extra stuff that I do not want. 2) It is not very clear to me as which Saxon API to use to add the XQuery results to the existing document. So that the resultant document looks as:
<?xml version="1.0" encoding="UTF-8"?>
<dataOfBooks:DataOfBooks xmlns:dataOfBooks="DataOfBooks">
<Id>ID123</Id>
<books>
<book>
<name>aaa</name>
<serialNo>99</serialNo>
</book>
<book>
<name>ccc</name>
<serialNo>77</serialNo>
</book>
</books>
</dataOfBooks:DataOfBooks>
One more question - I tried using dynamicContext and treeinfo classes since I though the usage of treeinfo API might be more optimal, but no luck. If you think, usage of TreeInfo API is efficient, I really appreciate a code example for my requirement. Your help is much appreciated.
Thanks in advance for your time and interest.
The fact that your XQuery code is producing unwanted namespaces is because your query is wrong, but we can't tell you how it is wrong unless you show us the code.
The result:sequence in your output suggests that you have somehow contrived to ask for output in "wrapped" format, which suggests some kind of misuse of Saxon APIs. Again, without seeing your code, we can't tell you exactly what you have done wrong.
To make small changes to an existing document, leaving the rest unchanged, I would normally recommend XSLT over XQuery. In XSLT 3.0, you can sort the books by name using the following stylesheet:
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="3.0">
<xsl:mode on-no-match="shallow-copy"/>
<xsl:template match="books">
<xsl:copy>
<xsl:perform-sort select="book">
<xsl:sort select="name"/>
</xsl:perform-sort>
</xsl:copy>
</xsl:template>
</xsl:transform>
In both XQuery and XSLT, the result of your query/transformation is a new document, which you can use in place of the original. If you want to make in-situ updates to an existing document, you can do this using XQuery Update; however Saxon does not support XQuery Update against documents in DOM4J format.
Saxon does allow you to capture the result of a query or transformation as a DOM4J Document, and you could use DOM4J APIs to graft this document (or rather, its outermost element) back into the original DOM4J document.
Later
You have now provided your code (you should have provided it as an edit to the original question, not as an answer).
I guess your DOMWriter is the DOM4J class of that name, which like much of DOM4J is rather badly documented. But I think it is copying the DOM4J tree to a DOM tree, which you definitely don't want to do. If you really want to copy the tree to make it convenient for Saxon, you should copy it to a Saxon tree, but for this use case it's best to leave it in DOM4J form. Use
DocumentBuilder builder = processor.newDocumentBuilder();
XdmNode inDoc = builder.wrap(dom4jdoc);
When you run your query, the resulting XdmValue will now be a sequence of XdmNode objects, each of which is a wrapper around a DOM4J Element node. These element nodes are still attached to the original DOM4J tree, and they still have their original namespaces. There is no need to serialize the result to lexical XML.
You can copy the result to a List value by writing
List<Element> sortedNodes = new ArrayList<Element>();
for (XdmItem item : result) {
sortedNodes.add(((Element)((XdmNode)item).getExternalNode()));
}
and then (if I read the DOM4J documentation correctly) you can replace the content of the containing books element with
Element books = (Element)sortedNodes.get(0).getParent();
List booksContent = books.elements();
booksContent.clear();
booksContent.addAll(sortedBooks);

What is wrong with the xml code

I got this question in an exam to specify what is wrong with this piece of XML code and I have no idea what the answer might be:
<contact id=”10” name=”randomName” email=”first.last#gmail.com” phone=”09090909”/>
I am guessing that the email would be prone to spam or something.
Original answer: you cannot name an attribute "id" in XML for your own purpose. It is a reserved name of the XML schema space which actually serves to identify a specific element to then do a look up by XML id.
EDIT
The issue with the XML might be a modeling one. The fact you use attributes for name, email, and phone means that a contact can only ever have one value of each. Maybe your professor is after a new model e.g.:
<contact id="10" name="foo">
<email>sdfsdfsdf</email>
<email>sdfsdfsdf</email>
</contact>
XML attributes are not intended to carry the object data.
Your XML should be structured like this:
<contact>
<id>10</id>
<name>randomName</name>
<email>first.last#gmail.com</email>
<phone>09090909</phone>
</contact>
Further reading: http://www.w3schools.com/xml/xml_attributes.asp

Reference to undeclared namespace prefix when parsing MSXML

How do I solve the
Reference to undeclared namespace prefix: '%s'
problem with Microsoft's msxml implementation?
I'm using an XML feed from a government web-site that contains values i need to parse. The xml contains namespaces:
<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns="http://purl.org/rss/1.0/"
xmlns:cb="http://www.cbwiki.net/wiki/index.php/Specification_1.1"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:dcterms="http://purl.org/dc/terms/"
xmlns:xsi="http://www.w3c.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.w3c.org/1999/02/22-rdf-syntax-ns#rdf.xsd">
<item rdf:about="http://www.bankofcanada.ca/stats/rates_rss/STATIC_IEXE0101.xml">
<cb:statistics>
<cb:exchangeRate>
<cb:value decimals="4">1.0351</cb:value>
<cb:baseCurrency>CAD</cb:baseCurrency>
<cb:targetCurrency>USD</cb:targetCurrency>
<cb:rateType>Bank of Canada noon rate</cb:rateType>
<cb:observationPeriod frequency="daily">2011-05-09T12:15:00-04:00</cb:observationPeriod>
</cb:exchangeRate>
</cb:statistics>
</item>
</rdf:RDF>
Running the XPath query:
/rdf:RDF/item/cb:statistics/cb:exchangeRate/cb:targetCurrency
fails with the error:
Reference to undeclared namespace prefix: 'rdf'
Edit:
If i edit the original XML to remove all use of namespaces:
<?xml version="1.0" encoding="ISO-8859-1"?>
<rdf>
<item>
<statistics>
<exchangeRate>
<value decimals="4">1.0351</value>
<baseCurrency>CAD</baseCurrency>
<targetCurrency>USD</targetCurrency>
<rateType>Bank of Canada noon rate</rateType>
<observationPeriod frequency="daily">2011-05-09T12:15:00-04:00</observationPeriod>
</exchangeRate>
</statistics>
</item>
</rdf>
The query /rdf/item/statistics/exchangeRate/baseCurrency doesn't fail, and returns nodes:
<baseCurrency>CAD</baseCurrency>
How do i get Microsoft XML to work with namespaces?
Edit 2
i've tried adding SelectionNamespaces to the DOMDocument object:
doc.setProperty('SelectionNamespaces', 'xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cb="http://www.cbwiki.net/wiki/index.php/Specification_1.1"');
Now the xpath query doesn't fail, but it also returns no nodes:
nodes = doc.selectNodes('/rdf:RDF/item/cb:statistics/cb:exchangeRate/cb:targetCurrency');
See also
“undeclared reference to namespace prefix ” error
XMLReader - How to handle undeclared namespace
PRB: Specifying Fully Qualified Element Names in XPath Queries
XPath not working properly.
Using SelectionNamespaces is the correct approach, you are just missing a namespace.
Notice that your XML document explicitly sets the default namespace as follows:
xmlns="http://purl.org/rss/1.0/"
This means that any element without a prefix, such as the item element, is actually in the default namespace. So if you want to select that element with an XPath expression, you must first set an appropriate selection namespace.
To do this, you can change your call to setProperty like so:
doc.setProperty('SelectionNamespaces', 'xmlns:rss="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cb="http://www.cbwiki.net/wiki/index.php/Specification_1.1"');
Here you've assigned the default namespace from the document to the rss: prefix in your XPath expression. With that change in place, the following XPath expression should work correctly:
nodes = doc.selectNodes('/rdf:RDF/rss:item/cb:statistics/cb:exchangeRate/cb:targetCurrency');
It works because it references the item element using the correct namespace. The fact that the prefix differs between the XPath expression and the original document is immaterial. It is the namespace which the prefix is bound to that matters.
doc.setProperty('SelectionNamespaces', 'xmlns:rss="http://purl.org/rss/1.0/" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:cb="http://www.cbwiki.net/wiki/index.php/Specification_1.1"');
Dont forget to load the xsd file or schema to the xmldoc object
is the way to go
I dont have enough reputation to comment. But that bit there saved me a lot of time.
Thank you so much
If you are using XMLSerializer and see this error, it is likely that you are running into the IE bug described here:
https://stackoverflow.com/a/11399681
It took me a lot of time to realize that this was happening, so I thought it best to link these two issues.

how to convert an XML into an array in asp.net

i need to convert an XML into an array using asp.net.
I tried searching on the web but couldn't find the proper solution.
Can someone please help me with this
Thanks,
Alloi
I find LinqToXML helpful in these circumstances. I have a very simple XML document:
<?xml version="1.0" encoding="utf-8" ?>
<People>
<Person>
<id>1</id>
<name>Dave</name>
</Person>
<Person>
<id>2</id>
<name>Pete</name>
</Person>
<Person>
<id>3</id>
<name>Marie</name>
</Person>
</People>
And the following code that will turn it into an array of objects:
FileStream file = new FileStream("data.xml", FileMode.Open);
XDocument xmldoc = XDocument.Load(file);
var people = (from p in xmldoc.Descendants("Person")
select new
{
ID = p.Element("id").Value,
Name = p.Element("name").Value
}).ToArray();
foreach (var person in people)
{
Console.WriteLine(person.Name);
}
I think LinqToXML might be helpful in your situation. Here's a sample in vb.net (I could not find anything in csharp...): http://msdn.microsoft.com/en-us/vbasic/bb738047.aspx#convnodesarray
If you have an XSD to which the XML refers to, that probably can be a starting point on the implementation of array (or list).
A way... might help you
Read xml into dataset.
Iterate dataset on the basis of table rows and add each row entity to a list.
display list as array or list itself.
I don't know if there is a .net class that performs this action directly or not but in case there is no you have 2 options :
1 . Load XML into dataset then loop in the dataset and set elements of the array.
2 . Get XML nodes number then loop through XML and read each node element then put it in the array.
Hope that this is useful
Alloi - i think that due to the complexity inherent in xml structures, this will very much depend on the xml itself. what i'm saying is that a generic solution may be very difficult to make bomb proof, whereas a solution to a 'known' structure may be fairly straightfwd. the serialiser may allow you to save it to ToList() or similar but beyond that, it's a case of horses for sources i think. :)
jim
[edit] - link that may be useful: http://www.c-sharpcorner.com/UploadFile/chauhan_sonu57/SerializingObjects07202006065806AM/SerializingObjects.aspx
also - a nice little link showing how to convert xml->json in c# : http://www.phdcc.com/xml2json.htm

XQuery - remove nodes based on its sub element being in the "ban" list

I am a total noob with XQuery, but before at start digging deep into it, i'd like to ask some experts advice about whether i am looking at the correct direction.
The problem:
A huge xml file which contains a whole lot of users and their access information (password access rights and so on) example below:
<user>
<name>JC1234</name>
<password>popstar</password>
<accesslevel>0</accesslevel>
</user>
<user>
<name>AHkl</name>
<password>Rudy10!</password>
<accesslevel>2</accesslevel>
</user>
i have a list of user names (csv file) that i need to remove from that huge xml files.
the result should be a new xml file wihtout those removed users....
is this feasable with XQuery?
any advice for a quick and dirty solution is welcomed!
There is no standard way of loading a CSV file in vanilla XQuery 1.0, although most implementations have an unparsed-text function or similar. If not the contents of the file can be passed in as a parameter.
The CSV file can be parsed using the tokenize function:
declare variable $names = tokenize(unparsed-text("banned.csv"), ",")
And the actual query is quite straightforward. Assuming your document is a a fragment containing just a list of <user /> nodes then the query is simply
doc("users.xml")/user[not(name=$names)]
If however the XML file contains a lot of other data then you may find XSLT's templating facilities more useful.

Resources