I have a xml in the following format.
<?xml version="1.0" encoding="UTF-8" standalone= "yes"?>
<rss>
<report name="rpt1">
<title>AAA</title>
<image></image>
<weblink></weblink>
<pdflink></pdflink>
<pdfsize></pdfsize>
</report>
<report name="rpt2">
<title>BBB</title>
<image>CCC</image>
<weblink>DDD</weblink>
<pdflink>EEE</pdflink>
<pdfsize>FFF</pdfsize>
</report>
</rss>
Now i want to iterate this xml and get the report node and from there get childnodes like title/pdflink/size etc which would be thru. looping using for loop. I want to use xmltextreader to accompalish this. I tried using while but i get only 1 loop after iterating. I dont know why. If thru for loop how do i iterate like,
for(loop when reader.element("reports)){} and then get the rest of the nodes and put them in an array or list or so. Once i get them stored in list i would want to dipaly them ina feed. which is a best way to do this? pls help.
In my case I was worried about the performance of loading a large document. What I have done is define a constructor on my objects to receive a XmlReader and hydrate - passing the reader back after it reaches a complete node.
This allows me to yield a populated object back as IEnumerable for each object as it's being read. Then I launch a new Task/Thread to handle processing that individual item and go back to processing the file.
private IEnumerable<Report> readReports(Stream reader)
{
var settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
var xmlReader = XmlReader.Create(reader, settings);
xmlReader.MoveToContent();
xmlReader.Read();
while (!xmlReader.EOF)
{
if (xmlReader.Name.ToUpper() == "report")
yield return new Report(xmlReader);
xmlReader.Read();
}
}
public Report(XmlReader reader) : this()
{
reader.MoveToContent();
if (reader.IsEmptyElement)
{
reader.Read();
return;
}
reader.Read();
while (!reader.EOF)
{
if (reader.IsStartElement())
{
switch (reader.Name.ToLower())
{
case "order_id":
this.OrderId = reader.ReadElementContentAsString();
break;
// abreviated the rest of the fields
default:
reader.Skip();
break;
}
}
else if (reader.Name.ToLower() == "report") //this watches for the end element of the container and returns after populating all properties
return;
}
}
I would definitly appreciate any feedback from the rest of the community, if there is a better approach or if there are any errors here please let me know.
Related
first post. I hope it meets with the rules of asking questions.
I'm in a bit of bother with an xml document (its an API returned Xml). Now it uses a multitude of internet (http based) security measures which I have worked thru and I am now able to return the the top tier of nodes that are not nested.
however there are a few nodes which are nested under these and I need to return some of these values.
I'm set on using XMLDocument to do this, and I'm not interested in using XPath.
I should also note that I'm using the .Net 4.5 environment.
Example XML
<?xml version="1.0" encoding="utf-8"?>
<results>
<Info xmlns="http://xmlns.namespace">
<title>This Title</title>
<ref>
<SetId>317</SetId>
</ref>
<source>
<name>file.xxx</name>
<type>thisType</type>
<hash>cc7b99599c1bebfc4b8f12e47aba3f76</hash>
<pers>65.97602</pers>
<time>02:20:02.8527777</time>
</source>
....... Continuation which is same as above
Ok so above is the Xml that gets returned from the API, now, I can return title node no problem. What I would also like to return is any of the node values in the Element, for example the pers node value. But I only want to return one (as there are many in the existing xml further down)
Please note that there is an xmlns in the Info node which may not be allowing me to return the values.
So here is my code
using (var response = (HttpWebResponse) request.GetResponse())
{
//Get the response stream
using (Stream stream = response.GetResponseStream())
{
if (stream != null)
{
var xDoc = new XmlDocument();
var nsm = new XmlNamespaceManager(xDoc.NameTable);
nsm.AddNamespace("ns", XmlNamespace);
//Read the response stream
using (XmlReader xmlReader = XmlReader.Create(stream))
{
// This is straight forward, we just need to read the XML document and return the bits we need.
xDoc.Load(xmlReader);
XmlElement root = xDoc.DocumentElement;
var cNodes = root.SelectNodes("/results/ns:Info", nsm);
//Create a new instance of Info so that we can store any data found in the Info Properties.
var info = new Info();
// Now we have a collection of Info objects
foreach (XmlNode node in cNodes)
{
// Do some parsing or other relevant filtering here
var title = node["title"];
if (title != null)
{
info.Title = title.InnerText;
_logger.Info("This is the title returned ############# {0}", info.Title);
}
//This is the bit that is killing me as i can't return the any values in the of the sub nodes
XmlNodeList sourceNodes = node.SelectNodes("source");
foreach (XmlNode sn in sourceNodes)
{
XmlNode source = sn.SelectSingleNode("source");
{
var pers = root["pers"];
if (pers != null) info.pers = pers.InnerText;
_logger.Info("############FPS = {0}", info.pers);
}
}
}
}
Thanks in advance for any help
So I finally figured it out.
Here is the code that gets the subnodes. Basically I wasn't using my namespace identifier or my namespace for returning subnodes within the "Source" node.
For anybody else in this situation,
When you declare your name space there are to parts to it, a namespace identifier which is anything you want it to be in my case I chose "ns" and then the actual namespace in the XML file which is prefixed by xmlns and will contain something like for example: "http://xmlns.mynamespace".
So when searching subnodes inside the top level you need to declare these namespaces for the main node of the subnode you want to get.
// get the <source> subnode using the namespace to returns all <source> values
var source = node.SelectSingleNode("ns:source", nsm);
if (source != null)
{
info.SourceType = source["type"].InnerText;
info.Pers = source["pers"].InnerText;
_logger.Info("This SourceNode is {0}", info.SourceType);
_logger.Info("This PersNode is {0}", info.FramesPerSecond);
}
I hope this helps somebody else that's chasing their tails as I have.
Thanks
XmlTextReader reader = new XmlTextReader("D://project_elysian//data.xml");
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
reader.Read();
//Response.Write(reader.Value + "</br>");
//Response.Write(reader.Depth);
switch (reader.Name)
{
case "Id": Response.Write(reader.Value + "</br>");
break;
case "Name": Response.Write(reader.Value + "</br>");
break;
}
}
}
I am trying to read data.xml file and display its contents of the specified tags, but the resultant page remains blank, and no compilation error is given, am stuck, can't figure out what is wrong with this code.
I suspect if you "View Source" on the resulting page you'll see the data you are expecting to see.
The problem is that your web browser sees these xml elements as unknown html tags and so doesn't know how to display them.
You need to "encode" your output, so your string is literally displayed as is.
Instead of writing:
Response.Write(reader.Value + "</br>");
try
Response.Write(Server.HtmlEncode(reader.Value) + "</br>");
What this does is replace < with < > with > and a few others. "<" tells the browser to render "<" rather than treat it as the beginning of a tag.
[Edit - in response to comment]
It sounds like your none of your cases are ever true. Without knowing the contents of the source xml file, it is hard to say - but have you tried putting a breakpoint on the Response.Writes in the cases? Are they ever hit?
If not, then this is not related to anything I mentioned above - but you are not getting what you expect from your reader.
Try starting with a small sample of the xml file and step through in the debugger. Try and determine what data (e.g. the reader.Name property) is present on the reader when you hit something you are interested in, and amend the switch statement accordingly.
[2nd Edit - in response to sample xml]
Your mistake is the Read() call just after the check for the XmlNodeType.Element. You are basically reading until you find an element (the Read() call in the while). Once you've found the element, you are then pushing past the element (the other Read() call) before trying to read the element name. This inner reader.Read() makes sure you are no longer on the element by the time you try to check its name.
Try this:
while (reader.Read())
{
if (reader.NodeType == XmlNodeType.Element)
{
// Capture the element name before pushing past it.
var elementName = reader.Name;
reader.Read();
//Response.Write(reader.Value + "</br>");
//Response.Write(reader.Depth);
switch (elementName)
{
case "Id":
Response.Write(reader.Value);
break;
case "Name":
Response.Write(reader.Value);
break;
}
}
}
The key to finding this sort of thing, is to debug carefully. Start with a cut down xml file and either actually step through in the debugger, or write debug output to a log or the response. It'll make identifying these sort of issues much easier.
Since it is working outside switch, I guess than name of the node is of different case.Check the case of the nodes
Edit:
You are calling reader.read() twice and the reader.value will not return proper value for an element.
If you still want to use xmlreader check the below code
XmlTextReader reader = new XmlTextReader(<XML Path>)
while (reader.Read())
{ if (reader.NodeType == XmlNodeType.Element)
{
switch (reader.Name)
{
case "Id": Response.Write(Server.HtmlEncode(reader.ReadString()) + "</br>");
break;
case "Name": Response.Write(Server.HtmlEncode(reader.ReadString()) + "</br>");
break;
}
}
}
reader.Close();
I would suggest that you take a look at XML (de)serialization instead of using XmlReader. .Net will verify the xml and you will more easily be able to debug the Xml input.
You simply create a class with fields mirroring your Xml structure and use the following code:
class searchResult
{
public List<item> itemList { get; set;}
}
A complex field class example
class item
{
public int Id { get; set; }
public string Name{ get; set; }
}
The actual work gets done like so:
XmlSerializer SerializerIn = new XmlSerializer(typeof(SerializeTest));
FileStream fs = new FileStream(#"C:\test.xml", FileMode.Open, FileAccess.Read, FileShare.Read);
SerializeTest loadTest = (SerializeTest)SerializerIn.Deserialize(fs);
fs.Close();
Where SerializeTest is the class your loading the xml into. It is much easier to work this way, because you never need to deal with the raw Xml unless it is invalid.
You can find more info here: http://www.codeproject.com/Articles/4491/Load-and-save-objects-to-XML-using-serialization
Probably a better tutorial: https://web.archive.org/web/20211020113423/https://www.4guysfromrolla.com/webtech/012302-1.shtml
I feel like I must be doing this wrong. I've got a function that is supposed to construct a query based on an API's description of available fields. Here's what I've got:
var query_fields = getQueryFieldsFor(sobject_name);
// Need query fields for the next statement, which actually does the query
public function getQueryFieldsFor(sObject:String):String{
//helper function to get queryfields for given sobject
var queryFields:String = '';
app.connection.describeSObject(sObject,
new mx.rpc.Responder(
function(result:DescribeSObjectResult):void{
var returnFields:String = '';
for ( var field:Field in result.fields ){
if(field.active){
returnFields.concat(field.name+',')
}
}
returnFields.slice(0, returnFields.length-1); //remove last comma
queryFields = returnFields;
}, function(error):void{
Alert.show('error in getQueryFieldsFor function');
})
);
return queryFields;
}
I know this doesn't work, and I think I understand why. However, I keep running into this type of issue and I believe I'm just thinking about it/designing it wrong. So what's a better pattern here? Would really appreciate any insight on this. Many thanks in advance.
It would be better to externalize your functions and execute your next line of code after the fact:
public function getQueryFieldsFor(sObject:String):String
{
var responder:Responder = new Responder( onResult, onFault);
app.connection.describeSObject(sObject, responder);
}
private function onResult(result:DescribeSObjectResult):void
{
var returnFields:String = '';
for ( var field:Field in result.fields ){
if(field.active){
returnFields.concat(field.name+',')
}
}
returnFields.slice(0, returnFields.length-1); //remove last comma
queryFields = returnFields;
}
Your main problem though is not the code, but a lack of thinking asynchronously. You cannot have a function called "getQueryFields" that will return it instantly. What you want to do is think in the request/response way. You're trying to get some data, a request is made to a service, gets the data back, updates a property which is then binded to a view which gets redrawn. This is the proper way to do any webapp.
It might be beneficial for you to also look at application frameworks like RobotLegs and Parsley since it helps you manage these situations. Parsley also has a task library which lets you perform several asynchronous task one after another.
I'm using this for my code, it outputs to the xml file perfectly, but it adds an ' = ' sign after the element name even though only one of my elements has an attribute.
I suppose I could do something like
if(reader.Getattribute != "")
// I made that up on the spot, I'm not sure if that would really work
{
Console.WriteLine("<{0} = {1}>", reader.Name, reader.GetAttribute("name"));
}
else
{
Console.WriteLine("<{0}>", reader.Name);
}
but is there a cleaner way to code that?
My code (without workaround)
using System;
using System.Xml;
using System.IO;
using System.Text;
public class MainClass
{
private static void Main()
{
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter w = XmlWriter.Create(#"Path\test.xml", settings);
w.WriteStartDocument();
w.WriteStartElement("classes");
w.WriteStartElement("class");
w.WriteAttributeString("name", "EE 999");
w.WriteElementString("Class_Name", "Programming");
w.WriteElementString("Teacher", "James");
w.WriteElementString("Room_Number", "333");
w.WriteElementString("ID", "2324324");
w.WriteEndElement();
w.WriteEndDocument();
w.Flush();
w.Close();
XmlReader reader = XmlReader.Create(#"Path\test.xml");
while (reader.Read())
{
switch (reader.NodeType)
{
case XmlNodeType.Element:
Console.WriteLine("<{0} = {1}>", reader.Name, reader.GetAttribute("name"));
break;
case XmlNodeType.Text:
Console.WriteLine(reader.Value);
break;
case XmlNodeType.CDATA:
Console.WriteLine("<[CDATA[{0}]>", reader.Value);
break;
case XmlNodeType.ProcessingInstruction:
Console.WriteLine("<?{0} {1}?>", reader.Name, reader.Value);
break;
case XmlNodeType.Comment:
Console.WriteLine("<!--{0}-->", reader.Value);
break;
case XmlNodeType.XmlDeclaration:
Console.WriteLine("<?xml version='1.0'?>");
break;
case XmlNodeType.Document:
break;
case XmlNodeType.DocumentType:
Console.WriteLine("<!DOCTYPE {0} [{1}]", reader.Name, reader.Value);
break;
case XmlNodeType.EntityReference:
Console.WriteLine(reader.Name);
break;
case XmlNodeType.EndElement:
Console.WriteLine("</{0}>", reader.Name);
break;
}
}
}
}
Output
<?xml version='1.0'?>
<classes = >
<class = EE 999>
<Class_Name = >
Programming
</Class_Name>
<Teacher = >
James
</Teacher>
<Room_Number = >
333
</Room_Number>
<ID = >
2324324
</ID>
</class>
</classes>
Because this line
case XmlNodeType.Element:
Console.WriteLine("<{0} = {1}>", reader.Name, reader.GetAttribute("name"));
break;
Always writes the '=' without checking.
A rough fix :
case XmlNodeType.Element:
Console.WriteLine("<{0}", reader.Name);
if (reader.HasAttributes)
// Write out attributes
Console.WriteLine(">");
break;
But why are you using the XmlReader at all? It is cumbersome and only useful when dealing with huge Xml streams.
If your datasets are not >> 10 MB then take a look at XDocument or XmlDocument
The XmlWriter in your Example can be replaced by (rough approx):
// using System.Xml.Linq;
var root = new XElement("classes",
new XElement("class", new XAttribute("name", "EE 999"),
new XElement("Class_Name", "Programming"),
new XElement("Teacher", "James")
));
root.Save(#"Path\test.xml");
var doc = XDocument.Load(#"Path\test.xml");
// doc is now an in-memory tree of XElement objects
// that you can navigate and query
And here is an intro
I don't know exactly what you're trying to accomplish but personally I would create a .NET class representing your class element with properties identifying the sub elements then use System.Xml.Serialization.XmlSerializer to write or read it from a file.
Here is an example:
using System.Xml.Serialization;
public class MyClasses : List<MyClass>{}
public class MyClass{
public String Teacher{ get; set; }
}
void main(){
MyClasses classList = new MyClasses();
MyClass c = new MyClass();
c.Teacher = "James";
classList.Add(c);
XmlSerializer serializer = new XmlSerializer(classList.GetType());
serializer.Serialize(/*Put your stream here*/);
}
And, after leaving setting up your stream as an exercise to the reader, blamo, you're done outputing an XML representation of your object to some stream. The stream could be a file, string, whatever. Sorry for nasty C# (if its nasty) I use VB.NET everyday so the syntax and keywords may be a little off.
Update
I added some code to show how to serialize a collection of the classes. If nodes aren't coming out named correctly there are attributes you can add to your class properties, just do a quick google for them.
Update again
Sorry, its hard to explain when we're using the same word to mean two different things. Lets say you're trying to represent a bucket of bricks. You would write a C# class called Brick and a C# class called Bucket that inherited from List<Brick> your Brick would have a property called Color. You would then make all your bricks with different colors and fill the bucket with your bricks. Then you would pass your bucket to the serializer and it would give you something like:
<Bucket>
<Brick>
<Color>
blue
</Color>
</Brick>
</Bucket>
The serializer builds the XML for you from the definitions of your classes so you don't have to worry about the details. You can read more about it here and here
In a Flex application I am trying to turn an Object into a QueryString such as name1=value1&name2=value2... But I am having trouble getting the names of the Objects children. How do I enumerate the names instead of the values?
Thanks
I'm guessing you're doing a for each(in) loop. Just do a normal for(in) loop and you'll get the names instead of the values:
for(var name:String in obj) {
var value:* = obj[name];
// do whatever you need
}
Ok, first off, if you need that query string to actually query a server, you don't really need to get it yourself as this code will query the server for you
protected function callSerivce():void
{
var o:Object = new Object();
o.action = "loadBogusData";
o.val1 = "dsadasd";
service.send(o);
}
<mx:HTTPService id="service" url="http://www.somewhere.com/file.php" method="GET" showBusyCursor="true"/>
Will make a call to the server like this: http://www.somewhere.com/file.php?action=loadBogusData&val1=dsadasd
But in case you really want to analyze the object by hand, try using ObjectUtil.getClassInfo, it returns a lot of information including all the fields (read more on LiveDocs).