I'm writing a Windows service in C#. I've got an XmlWriter which is contains the output of an XSLT transformation. I need to get the XML into an XMLElement object to pass to a web service.
What is the best way to do this?
You do not need an intermediate string, you can create an XmlWriter that writes directly into an XmlNode:
XmlDocument doc = new XmlDocument();
using (XmlWriter xw = doc.CreateNavigator().AppendChild()) {
// Write to `xw` here.
// Nodes written to `xw` will not appear in the document
// until `xw` is closed/disposed.
}
and pass xw as the output of the transform.
NB. Some parts of the xsl:output will be ignored (e.g. encoding) because the XmlDocument will use its own settings.
Well, an XmlWriter doesn't contain the output; typically, you have a backing object (maybe a StringBuilder or MemoryStream) that is the dumping place. In this case, StringBuilder is probably the most efficient... perhaps something like:
StringBuilder sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb))
{
// TODO write to writer via xslt
}
string xml = sb.ToString();
XmlDocument doc = new XmlDocument();
doc.LoadXml(xml);
XmlElement el = doc.DocumentElement;
If you provide a writer, you provide a repository where an output generator is transferring data, thus the replay of Richard is good, you don't really need a string builder to send data from a reader to an XmlDocument!
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 want to convert one xml file into another xml file using xslt.here, i can able to pass the input document to the XPathDocument and also save the output file in disk by passing outfile into XmlTextWriter.
But now my problem is... i have my input is in string format and i also want output as a string.Instead of passing the location of the input file, i want to pass string that contains the xml data.
so i have to pass string object into xpathDoccument in someway and also get the resultant xml file as a string.Instead of save the output as a file,i want output as a string.
XPathDocument xpathDoc = new XPathDocument("C:\\InputXml.xml");
XslCompiledTransform xslt = new XslCompiledTransform();
string xsltFile = "C:\\conversion.xslt";
xslt.Load(xsltFile);
string outputFile = "C:\\myHtml.html";
XmlTextWriter writer = new XmlTextWriter(outputFile, null);
xslt.Transform(xpathDoc, null, writer);
writer.Close();
Please guide me to get out of this issue...
XPathDocument accepts TextReader. You can give a stream as new XPathDocument(new StringReader(xmlstring)). Similarly XmlTextWriter accepts TextWriter. So you can pass a StringWriter.
--edit--
var sw = new StringWriter();
XmlTextWriter writer = new XmlTextWriter(sw);
xslt.Transform(xpathDoc, null, writer);
var str= sw.ToString();
Try this,
XslTransform xTrans = new XslTransform();
xTrans.Load(nodeXsltPath); //xsl file path
XmlDocument input= new XmlDocument();
XmlDocument output= new XmlDocument();
input.LoadXml(xmlString); /* Xml string to be loaaded */
output.Load(xTrans.Transform(input,null,new XmlUrlResolver()));
output.Save(filePathtoSave);
I am trying to write an XML response for my web service however I can't figure out how to make the declaration appear in the response.
My code is like so :
StringBuilder sBuilder = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sBuilder))
{
writer.WriteStartDocument();
writer.WriteStartElement("ReportResponse");
Response.WriteXml(writer);
writer.WriteEndElement();
writer.WriteEndDocument();
}
var response = XElement.Parse(sBuilder.ToString());
return response;
Response is just a POCO for storing response data.
I am aware that the Save method includes the declaration and the ToString() method does not. I need to write my declaration back with ToString().
I really just want to return custom XML from my REST Service without casting my string 100 times to return valid XML. Is this even possible or am just spinning my wheels ?
If you want to include xml declaration, you can do it this way:
XDocument xdoc = XDocument.Parse(xmlString);
StringBuilder builder = new StringBuilder();
using (TextWriter writer = new StringWriter(builder))
{
xdoc.Save(writer);
}
Console.WriteLine(builder);
Update:
I've noticed, that StringWriter spoils encoding. So one more option is to do so:
string docWithDeclaration = xdoc.Declaration + xdoc.ToString();
The code below is causing the " Data at the root level is invalid. Line 1, position 1. "
I like to keep the code indent(linebreak) but keep having the problem as mentioned above. I could use the TextReader to load the xml but it will remove the indent which i don't like it. If you know how to fix problem please let me know. Thanks
public XmlDocument MYXML()
{
XmlWriterSettings wSettings = new XmlWriterSettings();
wSettings.Indent = false;
wSettings.OmitXmlDeclaration = false;
MemoryStream ms = new MemoryStream();
XmlWriter xw = XmlWriter.Create(ms, wSettings);// Write Declaration
xw.WriteStartDocument();
// Write the root node
xw.WriteStartElement("Library");
// Write the books and the book elements
xw.WriteStartElement("Book");
xw.WriteStartAttribute("BookType");
xw.WriteString("Hardback");
xw.WriteEndAttribute();
xw.WriteStartElement("Title");
xw.WriteString("Door Number Three");
xw.WriteEndElement();
xw.WriteStartElement("Author");
xw.WriteString("O'Leary, Patrick");
xw.WriteEndElement();
xw.WriteEndElement();
xw.WriteEndElement();
// Close the document
xw.WriteEndDocument();
// Flush the write
xw.Flush();
Byte[] buffer = new Byte[ms.Length];
buffer = ms.ToArray();
string xmlOutput = System.Text.Encoding.UTF8.GetString(buffer);
//The next 3 line works fine but it will remove the Indent from the XmlWriterSettings
//TextReader tr = new StreamReader(ms);
//ms.Seek(0, SeekOrigin.Begin);
//xmlOutput = tr.ReadToEnd() + "";
//Can't nload the xmlOutput from buffer
XmlDocument xmldoc = new XmlDocument();
xmldoc.LoadXml(xmlOutput);
return xmldoc;
}
The XmlWriter is writing a utf-8 byte-order mark to the stream. Encoding.UTF8.GetString doesn't account for this (since it's only supposed to occur in files) so the first character of the string becomes an invalid, unprintable character, which is what XmlDocument.LoadXml chokes on.
EDIT: Since you said you want to create an XmlDocument so you can reuse it, I recommend one of the following:
If using .Net 3.5 or newer, use XDocument which is much easier to use (I recommend this).
Create the XmlDocument directly by constructing nodes and adding them to the tree.
Create the XmlDocument directly from the writer by using XPathNavigator (XmlWriter writer = doc.CreateNavigator.AppendChild())
Note that you can't easily add insignificant whitespace to an XmlDocument. Using XDocument and writing it to the output using doc.Save(Response.Output) is by far the easiest option if you want to have nicely formatted output.
I am processing an XML file (which does not contain any dtd or ent declarations) in C# that contains entities such as é and à. I receive the following exception when attempting to load an XML file...
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.LoadXml(record);
Reference to undeclared entity
'eacute'.
I was able to track down the proper ent file here. How do I tell XmlDocument to use this ent file when loading my XML file?
In versions of the framework prior to .Net 4 you use ProhibitDtd of an XmlReaderSettings instance.
var settings = new XmlReaderSettings();
settings.ProhibitDtd = false;
string DTD = #"<!DOCTYPE doc [
<!ENTITY % iso-lat1 PUBLIC ""ISO 8879:1986//ENTITIES Added Latin 1//EN//XML""
""http://www.oasis-open.org/docbook/xmlcharent/0.3/iso-lat1.ent"">
%iso-lat1;
]> ";
string xml = string.Concat(DTD,"<xml><txt>rené</txt></xml>");
XmlDocument xd = new XmlDocument();
xd.Load(XmlReader.Create(new MemoryStream(
UTF8Encoding.UTF8.GetBytes(xml)), settings));
From .Net 4.0 onward use the DtdProcessing property with a value of DtdProcessing.Parse which you set on the XmlTextReader.
XmlDocument xd = new XmlDocument();
using (var rdr = new XmlTextReader(new StringReader(xml)))
{
rdr.DtdProcessing = DtdProcessing.Parse;
xd.Load(rdr);
}
I ran into the same problem, and not wanting to modify my XML (or DTD), I decided to create my own XmlResolver to add entities on the fly.
My implementation actually reads entities from the config file, but this should be enough to do what you're asking for. In this example, I'm converting a right single curly quote into an apostrophe.
class XmlEntityResolver : XmlResolver {
public override object GetEntity(Uri absoluteUri,
string role,
Type ofObjectToReturn)
{
if (absoluteUri.toString() == "-//MY PUB ID") {
MemoryStream ms = new MemoryStream();
StreamWriter sw = new StreamWriter(ms);
sw.Write("<!ENTITY rsquo \"'\">");
sw.Flush();
ms.Position = 0;
return ms;
}
else {
return base.GetEntity(absoluteUri, role, ofObjectToReturn);
}
}
}
Then, when you declare your XmlDocument, just set the resolver prior to load.
XmlDocument doc = new XmlDocument();
doc.XmlResolver = new XmlEntityResolver();
doc.Load(XML_FILE);
é is not a valid XML entity by default whereas it is a valid HTML entity by default.
You would need to define é as a valid XML entity for XML parsing purposes.
EDIT:
To add a reference to your external ent file you need to do that within the XML file itself. Save the ent file to disk and place it within the same directory as the document being parsed.
<!ENTITY % stuff SYSTEM "iso-lat1.ent">
%stuff;
If you want to go a different route check out the information on ENTITY declaration.
According to this, you have to reference them within the file; you cannot tell LoadXml to do this for you.
Your question has been answered in 2004 itself at MSDN Article........ You can find it here.......
http://msdn.microsoft.com/en-us/library/aa302289.aspx