i am adding tracing for audit purposes of a simple process i have built as an .exe and set in the scheduler to run every 10 minutes. i want to have the application output the results into an xml file.
if the file exists then open and append data to it, if it does not exist i want to create a new xml file that will be persisted and used on next run.
here is my code now, what do i need to add, how do i open the xml file (on c:/file.xml) and use it to append nodes to?
static void Main(string[] args)
{
XmlDocument doc = new XmlDocument();
XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", null, null);
doc.AppendChild(dec);// Create the root element
XmlElement root = doc.CreateElement("STATS");
doc.AppendChild(root);
XmlElement urlNode = doc.CreateElement("keepalive");
urlNode.SetAttribute("runTime", DateTime.Now.ToString());
try
{
WebProxy wp = new WebProxy("http://proxy.ml.com:8083/");
WebClient w = new WebClient();
w.Proxy = wp;
if (w.DownloadString("http://wwww.example.com") != "")
urlNode.SetAttribute("result", "UP");
else
urlNode.SetAttribute("result", "DOWN");
}
catch
{
urlNode.SetAttribute("result", "DOWN");
}
finally
{
root.AppendChild(urlNode);
doc.Save("c:/keepAlive.xml");
}
}
You can't append an XML file - you'll have to load the file in memory , modify/add/etc, and then write it to disk.
EDIT :
Well, for loading a file you would use :
XmlDocument xmlDoc= new XmlDocument(); // create an xml document object.
if(System.IO.File.Exists("yourXMLFile.xml")
xmlDoc.Load("yourXMLFile.xml");// load from file
else{
// create the structure of your xml document
XmlElement root = xmlDoc.CreateElement("STATS");
xmlDoc.AppendChild(root);
}
and then start adding the keepalive stuff.
I would actually go a bit further and not mess around with xml. I'd create a class that contains everything I need and just serialize and deserialize it.
Like this:
[XmlRoot]
public class Stats{
public Stats(){}
public IList<StatsItem> Items{get;set;}
}
public class StatsItem{
public StatsItem(){}
public string UrlName{get;set;}
public DateTime Date{get;set;}
}
now just serialize this, and you have your xml document. When the time comes, deserialize it, add stuff to the Items list and serialize and save it to disk again.
There are lots of resources on google , so just search a bit for those.
using System;
using System.Xml.Linq;
using System.Xml.XPath;
...
public void Append(){
XDocument xmldoc = XDocument.Load(#"yourXMLFile.xml"));
XElement parentXElement = xmldoc.XPathSelectElement("yourRoot");
XElement newXElement = new XElement("test", "abc");
//append element
parentXElement.Add(newXElement);
xmldoc.Save(#"yourXMLFile.xml"));
}
Related
I have a .net Web Api 2 application that delivers data in XML.
My problem:
One of my classes looks like this:
public class Horse
{
public string Name { get;set; }
public string Category { get;set; }
}
When i serialize this, the result is:
<Horse>
<Name>Bobo</Name>
<Category>LargeAnimal</Category>
</Horse>
What i want is to wrap all outgoing XML content with a root element like this:
<Animal>
<Horse>
.....
</Horse>
</Animal>
I was hoping to do this in a custom XmlFormatter. But i can't seem to figure out how to append a root element on the writestream.
What is the best way to resolve this issue?
I have tried tweaking this answer to work in my custom xmlserializer, but doesn't seem to work. How to add a root node to an xml?
( I had a really short amount of time to write this question, so if anything is missing, please leave a comment.)
So.. Tweaked the answer to this question: How to add a root node to an xml? to work with my XmlFormatter.
The following code works, although i feel this is a hackish approach.
public override Task WriteToStreamAsync(Type type, object value, Stream writeStream, HttpContent content, TransportContext transportContext)
{
return Task.Factory.StartNew(() =>
{
XmlSerializer xs = new XmlSerializer(type);
XmlDocument temp = new XmlDocument(); //create a temporary xml document
var navigator = temp.CreateNavigator(); //use its navigator
using (var w = navigator.AppendChild()) //to get an XMLWriter
xs.Serialize(w, value); //serialize your data to it
XmlDocument xdoc = new XmlDocument(); //init the main xml document
//add xml declaration to the top of the new xml document
xdoc.AppendChild(xdoc.CreateXmlDeclaration("1.0", "utf-8", null));
//create the root element
var animal = xdoc.CreateElement("Animal");
animal.InnerXml = temp.InnerXml; //copy the serialized content
xdoc.AppendChild(animal);
using (var xmlWriter = new XmlTextWriter(writeStream, encoding))
{
xdoc.WriteTo(xmlWriter);
}
});
}
I created a C# Application to write data to xml file. It overrides all the new Data,
How to Avoid..?
Please help me.
This is my code
namespace BarcodeScaner
{
class WriteFile
{
DateTime dt=DateTime.Now;
public WriteFile()
{
}
public void createFile(string ptr,string value)
{
using (XmlWriter writer = XmlWriter.Create("Data.xml"))
{
writer.WriteStartDocument();
writer.WriteStartElement("Products");
writer.WriteStartElement("Details");
writer.WriteAttributeString("PTR", ptr);
writer.WriteAttributeString("Value", value);
writer.WriteAttributeString("DateTime", dt.ToString());
writer.WriteEndElement();
writer.WriteEndDocument();
}
}
}
}
You can use XmlDocument to add custom nodes to an existing Xml Document:
XmlDocument doc = new XmlDocument();
doc.Load("Data.xml");
XmlElement el = doc.CreateElement("child");
el.InnerText = "Example of data being appendeed";
doc.DocumentElement.AppendChild(el);
doc.Save("test.xml");
You need to read the existing file, add your data to it. And then write the combined results to the file.
Because of the format of XML files, you can't simply append new data to the file as you might with some file formats.
I'm assuming that what you're wanting to do is add new data to an existing XML file, correct?
You can't just append data to an XML file, and your code actually creates a new file each time it's run, overwriting the old one.
What you need to do is read the XML data into memory, add the new nodes, and write the whole file out again. If there is a lot of data then it may be more efficient to stream from one file to another and insert the new nodes as you go.
I am building a Windows 8 app, and I need to extract the whole XML node and its children as string from a large xml document, and the method that does that so far looks like this:
public string GetNodeContent(string path)
{
XmlReaderSettings settings = new XmlReaderSettings();
settings.IgnoreWhitespace = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
settings.IgnoreComments = true;
using (XmlReader reader = XmlReader.Create("something.xml", settings))
{
reader.MoveToContent();
reader.Read();
XmlDocument doc = new XmlDocument();
doc.LoadXml(reader.ReadOuterXml());
IXmlNode node = doc.SelectSingleNode(path);
return node.InnerText;
}
}
When I pass any form of xpath, node gets the value of null. I'm using the reader to get the first child of root node, and then use XMLDocument to create one from that xml. Since it's Windows 8, apparently, I can't use XPathSelectElements method and this is the only way I can't think of. Is there a way to do it using this, or any other logic?
Thank you in advance for your answers.
[UPDATE]
Let's say XML has this general form:
<nodeone attributes...>
<nodetwo attributes...>
<nodethree attributes... />
<nodethree attributes... />
<nodethree attributes... />
</nodetwo>
</nodeone >
I expect to get as a result nodetwo and all of its children in the form of xml string when i pass "/nodeone/nodetwo" or "//nodetwo"
I've come up with this solution, the whole approach was wrong to start with. The problematic part was the fact that this code
reader.MoveToContent();
reader.Read();
ignores the namespace by itself, because it skips the root tag. This is the new, working code:
public static async Task<string> ReadFileTest(string xpath)
{
StorageFolder folder = await Package.Current.InstalledLocation.GetFolderAsync("NameOfFolderWithXML");
StorageFile xmlFile = await folder.GetFileAsync("filename.xml");
XmlDocument xmldoc = await XmlDocument.LoadFromFileAsync(xmlFile);
var nodes = doc.SelectNodes(xpath);
XmlElement element = (XmlElement)nodes[0];
return element.GetXml();
}
I have an XML document that contains the following structure:
Its more or less a collection of Events:
<Events>
<Event>
<DateTime></DateTime>
<EventType></EventType>
<Result></Result>
<Provider></Provider>
<ErrorMessage></ErrorMessage>
<InnerException></InnerException>
</Event>
</Events>
In C# I have a persistent object called Event:
Now given that the document already exists, and saved to file... I call :
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(dataPath);
Now how can I add a new Event item to events?
I've got a strongly typed Event item in my C# code, and want it inserted into the Events collection in the XML object as last child.
I guess this is what I am really asking : https://stackoverflow.com/questions/1457033/c-insert-a-strongly-typed-object-as-node-in-existing-xml-document
Take a look at the Xml Serialization attributes.
You can do this:
[XmlRoot("Event")]
public class Event
{
[XmlElement("DateTime")]
public string DateTime
{
get;
set;
}
[XmlElement("EventType")]
public EnumReportingEventType EventType
{
get;
set;
}
[XmlElement("Result")]
public EnumReportingResult Result
{
get;
set;
}
[XmlElement("Provider")]
public string Provider
{
get;
set;
}
[XmlElement("ErrorMessage")]
public string ErrorMessage
{
get;
set;
}
[XmlElement("InnerException")]
public string InnerException
{
get;
set;
}
}
In fact, if the properties of your class have the same name as the elements in your Xml, you do not have to use the XmlElement attributes.
Then, you can use the XmlSerializer to serialize and deserialize.
Edit:
Then, wouldn't it be better to create a type which represent the entire type that is stored in the existing xml ?
Deserialize it, give a value to the additional property, and serialize it back ?
Just hand code it using the XmlDocument. You can add a class that does the conversion or insertion into the document you are going to save.
This is based on the restriction of .net 2.0 and what you said in these comments:
#Fred, I want to try minimize the write time, hence the reason for not writing all at once, the less write time in this app, the less chance of file corruption. – JL 16 mins ago
Why do you think you have a lot of chance of file corruption ? – Frederik Gheysels 9 mins ago
From existing test results on code I already have using serialization as a whole.
If you are using .Net 3.5 you can use Linq to XML, something like the following will work
XDocument doc = new XDocument(
new XDeclaration("1.0", "utf-8", "yes"),
new XComment("Event document"),
new XElement("Events",
new XElement ("Event",
new XElement("DateTime", event.DateTime),
new XElement("EventType", event.EventType),
new XElement("Result", event.Result),
new XElement("Provider", event.Provider),
new XElement("ErrorMessage", event.ErrorMessage),
new XElement("InnerException", event.InnerException)
)
));
doc.Save(#"c:\sample.xml");
If you have an existing xml document that you want to append to somthing like the following is required.
XDocument doc = XDocument.Load(#"c:\sample.xml");
XElement events = doc.Element(XName.Get("Events"));
events.Add(new XElement ("Event",
new XElement("DateTime", event.DateTime),
new XElement("EventType", event.EventType),
new XElement("Result", event.Result),
new XElement("Provider", event.Provider),
new XElement("ErrorMessage", event.ErrorMessage),
new XElement("InnerException", event.InnerException)
));
doc.Save(#"c:\sample.xml");
Insert a XmlElement field near the end of the "Event" class like so:
[System.Xml.Serialization.XmlAnyElementAttribute()]
public System.Xml.XmlElement Any { get; set }
You can name it whatever you want as long as you have the "XmlAnyElementAttribute" on it.
You can use something like the following to serialize a strongly-typed object into this field:
MyParentObject parent = new MyParentObject(){ ... };
MyObject obj = new MyObject(){ /*... initialize*/ };
XmlSerializer ser = new XmlSerializer(typeof(MyObject));
XmlDocument doc = new XmlDocument();
using (StringWriter sw = new StringWriter())
{
ser.Serialize(sw, obj);
doc.LoadXml(sw.ToString());
}
parent.Any = (XmlElement)doc.DocumentElement;
The serialized XML will append nicely to your class, it will event include the correct namespaces.
Assuming that your Event class can already be serialized the way you want using XmlSerializer, you can do the following:
XmlSerializer ser = new XmlSerializer(typeof(Event));
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load(dataPath);
Event evt = ...;
XmlDocument evtDoc = new XmlDocument();
using (XmlWriter writer = evtDoc.CreateNavigator().AppendChild())
{
ser.Serialize(writer, evt);
}
XmlNode evtNode = evtDoc.RemoveChild(evtDoc.DocumentElement);
XmlNode events = xmlDoc.SelectSingleNode("/Events");
events.AppendChild(evtNode);
What you are looking to do is something like:
doc.ChildNode[0].AppendChild(MethodToReturnADeserializedObject(event));
Create a method to deserialize the event object into an xml node. Then use AppendChild to insert that as the last element amongst it's child nodes.
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.