I have a C# service where I loop every 1 seconds trough a directory looking for XML files.
These XML files may look like this:
<?xml version="1.0" encoding="UTF-8"?>
<job>
<type>freelance</type>
<text>blah</text>
</job>
In a foreach I do the following:
var doc = new XmlDocument();
doc.LoadXml(xmlFile);
XmlNode xmltype = doc.DocumentElement.SelectSingleNode("/job/type");
And than I would like to use these strings to use in my program, however. Using xmltype.InnerText does not work. Documentation on MSDN does not provide me with anything new and I would like to know what I am doing wrong.
First you have to check the xml file.whether is there any data or not.
after that take the one particular node check for innerText.
for example
This is the Text
XmlNode xmlType = doc.DocumentElement.SelectSingleNode("/job/type");
xmlType.innerText = "This is the Text";
xmlType.Value = "Stack";
This following console program will output "freelance". I think the issue may be with some of your XML - do all of your XML docs follow the same schema? I am guessing that the code fails with a NullReferenceException at some point. I've added a null check to protect against this possible scenario.
To help debug your service I tend to use the technique described here to run the app as console application (for easy debugging) or windows service.
using System;
using System.Xml;
public class Program
{
static string xmlFile = #"<?xml version=""1.0"" encoding=""UTF-8""?>
<job>
<type>freelance</type>
<text>blah</text>
</job>";
public static void Main()
{
var doc = new XmlDocument();
doc.LoadXml(xmlFile);
XmlNode xmltype = doc.DocumentElement.SelectSingleNode("/job/type");
if(xmltype==null)
{
Console.WriteLine("/job/type not found");
} else {
Console.WriteLine(xmltype.InnerText);
}
}
}
Try this:
string str = xmltype.Value;
Related
I have to generate specific XML data from code.
The XML needs to look like this
<this:declarationIdentifier xmlns:this="demo.org.uk/demo/DeclarationGbIdentifier"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="demo.org.uk/demo/DeclarationGbIdentifier DeclarationGbIdentifier.xsd"
xmlns:nsIdentity="demo.org.uk/demo/DeclarationGbIdentityType">
<this:declarationIdentity>
<nsIdentity:declarationUcr>Hello World</nsIdentity:declarationUcr>
</this:declarationIdentity>
</this:declarationIdentifier>
I have dabbled with XmlSerializer and XDocument but cant get the output to match this exactly
Please help.
I believe this will produce your desired output. There possibly is a simpler way this is just off the cuff to get you started. With the prefixes that you are requiring I would look up XmlDocument and adding namespaces to it to have a better understanding of what the code below is doing. Also what I would do is attempt to acquire the XSD schema file and use the XSD.exe to build a .cs file and then you can move forward with the XmlSerializer. If you move forward with the code below i highly suggest moving off your namespaceuri's into some soft of settings file so you can easily modify them in the event they change.
XmlDocument doc = new XmlDocument();
XmlElement root = doc.CreateElement("this", "declarationIdentifier", "demo.org.uk/demo/DeclarationGbIdentifier");
root.SetAttribute("xmlns:this", "demo.org.uk/demo/DeclarationGbIdentifier");
root.SetAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
//Just setting an Attribute of xsi:schemaLocation it would always drop the xsi prefix in the xml so this is different to accomodate that
XmlAttribute schemaAtt = doc.CreateAttribute("xsi", "schemaLocation", "http://www.w3.org/2001/XMLSchema-instance");
schemaAtt.Value = "demo.org.uk/demo/DeclarationGbIdentifier DeclarationGbIdentifier.xsd";
root.Attributes.Append(schemaAtt);
root.SetAttribute("xmlns:nsIdentity", "demo.org.uk/demo/DeclarationGbIdentityType");
doc.AppendChild(root);
XmlElement declarationIdentity = doc.CreateElement("this", "declarationIdentity", "demo.org.uk/demo/DeclarationGbIdentifier");
XmlElement declarationUcr = doc.CreateElement("nsIdentity","declarationUcr","demo.org.uk/demo/DeclarationGbIdentityType");
declarationUcr.InnerText = "Hello World";
declarationIdentity.AppendChild(declarationUcr);
doc.DocumentElement.AppendChild(declarationIdentity);
To output this as a string or dump it off to a file you can use the following operations, I output to a file as well as output to the console in my test app.
using (var stringWriter = new StringWriter())
using (StreamWriter writer = new StreamWriter(#"C:\<Path to File>\testing.xml"))
using (var xmlTextWriter = XmlWriter.Create(stringWriter))
{
doc.WriteTo(xmlTextWriter);
xmlTextWriter.Flush();
writer.Write(stringWriter.GetStringBuilder().ToString());
Console.WriteLine(stringWriter.GetStringBuilder().ToString());
}
I am trying to log some XML responses from a WCF Service using log4net.
I want the output of the XML file to the log to be in properly formed XML. The request comes in as an XMLElement.
Example:
The request comes in as this:
<?xml version="1.0" encoding="utf-8"?>
<ApplicationEvent xmlns="http://courts.wa.gov/INH_TV/ApplicationEvent.xsd">
<Severity xmlns="">Information</Severity>
<Application xmlns="">Application1</Application>
<Category xmlns="">Timings</Category>
<EventID xmlns="">1000</EventID>
<DateTime xmlns="">2012-09-02T12:05:15.234Z</DateTime>
<MachineName xmlns="">Server1</MachineName>
<MessageID xmlns="">10000000-0000-0000-0000-000000000000</MessageID>
<Program xmlns="">Progam1</Program>
<Action xmlns="">Entry</Action>
<UserID xmlns="">User1</UserID>
</ApplicationEvent>
Then if I output this value to log4net.
logger.Info(request.OuterXml);
I get the entire document logged in a single line like so:
<ApplicationEvent xmlns="http://courts.wa.gov/INH_TV/ApplicationEvent.xsd"><Severity xmlns="">Information</Severity><Application xmlns="">Application1</Application><Category xmlns="">Timings</Category><EventID xmlns="">1000</EventID><DateTime xmlns="">2012-09-02T12:05:15.234Z</DateTime><MachineName xmlns="">Server1</MachineName><MessageID xmlns="">10000000-0000-0000-0000-000000000000</MessageID><Program xmlns="">Progam1</Program><Action xmlns="">Entry</Action><UserID xmlns="">User1</UserID></ApplicationEvent>
I would like it to display in the log.txt file formatted correctly as it came in. So far the only way I have found to do this is to convert it to an XElement like so:
XmlDocument logXML = new XmlDocument();
logXML.AppendChild(logXML.ImportNode(request, true));
XElement logMe = XElement.Parse(logXML.InnerXml);
logger.Info(logMe.ToString());
This doesn't seem like good programming to me. I have been searching the documentation and I can't find a built-in way to output this correctly without converting it.
Is there an obvious, better way that I am just missing?
edit1: Removed ToString() since OuterXML is a String value.
edit2: I answered my own question:
So I did some more research, and I guess I missed a piece of code in the documentation.
http://msdn.microsoft.com/en-us/library/system.xml.xmlnode.outerxml.aspx
I have it down to:
using (MemoryStream ms = new MemoryStream())
{
XmlWriterSettings xws = new XmlWriterSettings();
xws.Indent = true;
using (XmlWriter xmlWriter = XmlWriter.Create(ms, xws))
{
request.WriteTo(xmlWriter);
}
ms.Position = 0; StreamReader sr = new StreamReader(ms);
string s = sr.ReadToEnd(); // s will contain indented xml
logger.Info(s);
}
Which is a little more efficient than my current method despite being more verbose.
XElement parse is the cleanest way. You can save a line or two with:
logger.Info(XElement.Parse(request.OuterXml).ToString());
I'm reading a file like from the web:
<?xml version='1.0' encoding='UTF-8'?>
<eveapi version="2">
<currentTime>2011-07-30 16:08:53</currentTime>
<result>
<rowset name="characters" key="characterID" columns="name,characterID,corporationName,corporationID">
<row name="Conqrad Echerie" characterID="91048359" corporationName="Federal Navy Academy" corporationID="1000168" />
</rowset>
</result>
<cachedUntil>2011-07-30 17:05:48</cachedUntil>
</eveapi>
im still new to XML and i see there are many ways to read XML data, is there a certain way im going to want to do this? what i want to do is load all the data into a StreamReader? and then use get; set; to pull the data later?
If you want object-based access, put the example xml in a file and run
xsd.exe my.xml
xsd.exe my.xsd /classes
this will create my.cs which is an object model similar to the xml that you can use with XmlSerializer:
var ser = new XmlSerializer(typeof(eveapi));
var obj = (eveapi)ser.Deserialize(source);
Use XmlReader Class or XmlTextReader Class
http://msdn.microsoft.com/en-us/library/aa720470(v=vs.71).aspx
http://msdn.microsoft.com/en-us/library/system.xml.xmltextreader(v=vs.71).aspx
If you need to use the data in the easy way, especially when you're new to XML, use XmlDocument.
To load the document:
using System.Xml;
using System.IO;
public class someclass {
void somemethod () {
//Initiate the XmlDocument object
XmlDocument xdoc;
//To load from file
xdoc.Load("SomeFolder\\SomeFile.xml");
//Or to load from XmlTextReader, from a file for example
FileStream fs = FileStream("SomeFolder\\SomeFile.xml", FileMode.Open, FileAccess.Read);
XmlTextReader reader = new XmlTextReader(fs);
xdoc.Load(reader);
//In fact, you can load the stream directly
xdoc.Load(fs);
//Or, you can load from a string
xdoc.LoadXml(#"<rootElement>
<element1>value1</element1>
<element2>value2</element2>
</rootElement>");
}
}
I personally find XmlDocument far easier to use for navigating an Xml file.
To use it efficiently, you need to learn XPath. For example, to get the name of the first row:
string name = xdoc.SelectSingleNode("/eveapi/result/rowset/row").Attribute["name"].InnerText;
or even more XPath:
string name = xdoc.SelectSingleNode("/eveapi/result/rowset/row/#name").InnerText;
you can even filter:
XmlNodeList elems = xdoc.SelectNodes("//*[#name=\"characters\"]")
gives you the rowset element.
But that's off topic.
I need to add the following code to the beginning of an XML file, while creating it:
<?xml version="1.0" encoding="ISO-8859-1"?>
<?xml-stylesheet type="text/xsl" href="colors.xslt"?>
I'm sure there is a method for this, but I haven't found it yet. I'm using C#.
Thank you
XmlDocument.CreateProcessingInstruction Method
public static void Main()
{
var doc = new XmlDocument();
doc.AppendChild(doc.CreateProcessingInstruction(
"xml-stylesheet",
"type='text/xsl' href='colors.xslt'"));
}
For LINQ XDocument you can use this:
XDocument xDoc = new XDocument();
xDoc.Add(new XProcessingInstruction("xml-stylesheet",
"type=\"text/xsl\" href=\"xml-style.xslt\""));
How do i know if my XML file has data besides the name space info:
Some of the files contain this:
<?xml version="1.0" encoding="UTF-8"?>
And if i encounter such a file, i want to place the file in an error directory
You could use the XmlReader to avoid the overhead of XmlDocument. In your case, you will receive an exception because the root element is missing.
string xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
using (StringReader strReader = new StringReader(xml))
{
//You can replace the StringReader object with the path of your xml file.
//In that case, do not forget to remove the "using" lines above.
using (XmlReader reader = XmlReader.Create(strReader))
{
try
{
while (reader.Read())
{
}
}
catch (XmlException ex)
{
//Catch xml exception
//in your case: root element is missing
}
}
}
You can add a condition in the while(reader.Read()) loop after you checked the first nodes to avoid to read the entire xml file since you just want to check if the root element is missing.
I think the only way is to catch an exception when you try and load it, like this:
try
{
System.Xml.XmlDocument doc = new System.Xml.XmlDocument();
doc.Load(Server.MapPath("XMLFile.xml"));
}
catch (System.Xml.XmlException xmlEx)
{
if (xmlEx.Message.Contains("Root element is missing"))
{
// Xml file is empty
}
}
Yes, there is some overhead, but you should be performing sanity checks like this anyway. You should never trust input and the only way to reliably verify it is XML is to treat it like XML and see what .NET says about it!
XmlDocument xDoc = new XmlDocument();
if (xDoc.ChildNodes.Count == 0)
{ // xml document is empty }
if (xDoc.ChildNodes.Count == 1)
{ // in xml document is only declaration node. (if you are shure that declaration is allways at the begining }
if (xDoc.ChildNodes.Count > 1)
{ // there is declaration + n nodes (usually this count is 2; declaration + root node) }
Haven't tried this...but should work.
try
{
XmlDocument doc = new XmlDocument();
doc.Load("test.xml");
}
catch (XmlException exc)
{
//invalid file
}
EDIT: Based on feedback comments
For large XML documents see Thomas's answer. This approach can have performance issues.
But, if it is a valid xml and the program wants to process it then this approach seems better.
If you aren't worried about validity, just check to see if there is anything after the first ?>. I'm not entirely sure of the C# syntax (it's been too long since I used it), but read the file, look for the first instance of ?>, and see if there is anything after that index.
However, if you want to use the XML later or you want to process the XML later, you should consider PK's answer and load the XML into an XmlDocument object. But if you have large XML documents that you don't need to process, then a solution more like mine, reading the file as text, might have less overhead.
You could check if the xml document has a node (the root node) and check it that node has inner text or other children.
As long as you aren't concerned with the validity of the XML document, and only want to ensure that it has a tag other than the declaration, you could use simple text processing:
var regEx = new RegEx("<[A-Za-z]");
bool foundTags = false;
string curLine = "";
using (var reader = new StreamReader(fileName)) {
while (!reader.EndOfStream) {
curLine = reader.ReadLine();
if (regEx.Match(curLine)) {
foundTags = true;
break;
}
}
}
if (!foundTags) {
// file is bad, copy.
}
Keep in mind that there's a million other reasons that the file may be invalid, and the code above would validate a file consisting only of "<a". If your intent is to validate that the XML document is capable of being read, you should use the XmlDocument approach.