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());
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'm trying to create an XML with multiple root elements. I can't change that because that is the way I'm supposed to send the XML to the server. This is the error I get when I try to run the code:
System.InvalidOperationException: This operation would create an incorrectly structured document.
Is there a way to overwrite this error and have it so that it ignores this?
Alright so let me explain this better:
Here is what I have
XmlDocument doc = new XmlDocument();
doc.LoadXml(_application_data);
Now that creates the XML document and I can add a fake root element to it so that it works. However, I need to get rid of that and convert it into a DocumentElement object.
How would I go about doing that?
Specify Fragment when creating XmlWriter as shown here
XmlWriterSettings settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.ConformanceLevel = ConformanceLevel.Fragment;
settings.CloseOutput = false;
// Create the XmlWriter object and write some content.
MemoryStream strm = new MemoryStream();
using (XmlWriter writer = XmlWriter.Create(strm, settings))
{
writer.WriteElementString("orderID", "1-456-ab");
writer.WriteElementString("orderID", "2-36-00a");
writer.Flush();
}
If it has multiple root elements, it's not XML. If it resembles XML in other ways, you could place everything under a root element, then when you send the string to the server, you just combine the serialized child elements of this root element, or as #Austin points out, use an inner XML method if available.
just create an XML with single root then get it's content as XML text.
you are talking about XML fragment anyways, since good xml has only one root.
this is sample to help you started:
var xml = new XmlDocument();
var root = xml.CreateElement("root");
root.AppendChild(xml.CreateElement("a"));
root.AppendChild(xml.CreateElement("b"));
Console.WriteLine(root.InnerXml); // outputs "<a /><b />"
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 have an XML file that I deserialize, the funny part is the XML file is the was serialized
using the following code:
enter code here
var serializer = new XmlSerializer(typeof(CommonMessage));
var writer = new StreamWriter("OutPut.txt");
serializer.Serialize(writer, commonMessage);
writer.Close();
And i m trying to deserialized it again to check if the output match the input.
anyhow here is my code to deserialize:
var serializer = new XmlSerializer(typeof(CommonMessage));
var reader = new StringReader(InputFileName);
CommonMessage commonMessage = (CommonMessage)serializer.Deserialize(reader);
Replace StringReader with StreamReader and it will work fine. StringReader reads value from the string (which is file name in your case).
I just had the same error message but different error source. In case someone has the same problem like me. I chopped off the very first char of my xml string by splitting strings. And the xml string got corrupted:
"?xml version="1.0" encoding="utf-16"?> ..." // my error
"<?xml version="1.0" encoding="utf-16"?> ..." // correct
(1,1) means basically first char of the first line is incorrect and the string can't be deserialized.
include in your CommonMessage class the XmlRoot element tag with your xmlroot eg:[XmlRoot("UIIVerificationResponse")]
You should disable the order mark in the StreamWriter constructor like this:
UTF8Encoding(false)
Full sample:
using (MemoryStream stream = new MemoryStream())
using (StreamWriter writer = new StreamWriter(stream, new UTF8Encoding(false)))
{
xmlSerializer.Serialize(writer, objectToSerialize, ns);
return Encoding.UTF8.GetString(stream.ToArray());
}
Let's say I have a System.Xml.XmlDocument whose InnerXml is:
<comedians><act id="1" type="single" name="Harold Lloyd"/><act id="2" type="duo" name="Laurel and Hardy"><member>Stan Laurel</member><member>Oliver Hardy</member></act></comedians>
I'd like to format it thusly, with newlines and whitespace added:
<comedians>
<act id="1" type="single" name="Harold Lloyd"/>
<act id="2" type="duo" name="Laurel and Hardy">
<member>Stan Laurel</member>
<member>Oliver Hardy</member>
</act>
</comedians>
I looked in the XmlDocument class for some prettifying method, but couldn't find one.
Basically you can use XmlDocument.Save(Stream) and pass any Stream as target to receive the xml content. Including a "memory-only" StringWriter as below:
string xml = "<myXML>";
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
using(StringWriter sw = new StringWriter())
{
doc.Save(sw);
Console.Write(sw.GetStringBuilder().ToString());
}
Update: using block
Jon Galloway posted an answer and deleted it, but it was actually the best option for me. It went somewhat like this:
StringBuilder Prettify(XmlDocument xdoc)
{
StringBuilder myBuf = new StringBuilder();
XmlTextWriter myWriter = new XmlTextWriter(new StringWriter(myBuf));
myWriter.Formatting = Formatting.Indented;
xdoc.Save(myWriter);
return myBuf;
}
No need to create a XmlWriterSettings object.
Trying to prettify XML is like putting lipstick on a pig :) It still ain't gonna be pretty.
Why are you trying to prettify it? If it is for editing purposes, Visual Studio does a pretty good job of presenting it in readable form. If it is for a user, then they may prefer to just open up in their preferred editor or explorer.