C# - Append an XElement array to XElement - c#

I have a c# application, where I'm doing a data compare of two xml files inside a method called RevisionTree. I return a list of elements(XElement) from this method. From the BuildXml method, call that method and save the list as tree. Next I create an xml root XElement. I then loop over each element from tree and add specified descendants (status, msg, date) to the root element, each one of these are XElement. So i should see an xml doument with root, then a list of repeating xml. However, when i try to save the to the writer i get the following error.
Error
Exception thrown: 'System.InvalidOperationException' in System.Private.Xml.dll
An unhandled exception of type 'System.InvalidOperationException' occurred in System.Private.Xml.dll
Token StartDocument in state Document would result in an invalid XML document.
Code
{
IEnumerable<XElement>
var tree = RevisionTree("C:\\Users\\Owner\\source\\repos\\SvnCore\\SvnCore\\old_logs.xml", "C:\\Users\\Owner\\source\\repos\\SvnCore\\SvnCore\\new_logs.xml");
using (XmlWriter writer = XmlWriter.Create("C:\\Users\\Owner\\source\\repos\\SvnCore\\SvnCore\\Temp.xml", xmlSettings))
{
writer.WriteStartDocument();
var root = new XElement("root");
foreach (var node in tree)
{
root.Add(new XElement("id", node.FirstAttribute));
root.Add(node.Descendants("status").FirstOrDefault());
root.Add(node.Descendants("msg").FirstOrDefault());
root.Add(node.Descendants("date").FirstOrDefault());
}
root.Save(writer);
writer.WriteEndElement();
writer.WriteEndDocument();
}
return true;
}

XElement.Save produces an entire document on its own -- you need XElement.WriteTo, which does not. So either (simplified):
var sb = new StringBuilder();
using (var sw = new StringWriter(sb))
using (XmlWriter writer = XmlWriter.Create(sw)) {
var root = new XElement("root");
root.Add(new XElement("id", "1"));
root.Save(writer); // no DocumentStart, no ElementStart
}
<?xml version="1.0" encoding="utf-16"?><root><id>1</id></root>
or (if you wanted to write multiple elements, or for some other reason want to control the document node yourself):
using (XmlWriter writer = XmlWriter.Create(sw)) {
writer.WriteStartDocument();
writer.WriteStartElement("root");
var notRoot = new XElement("notRoot");
notRoot.Add(new XElement("id", "1"));
notRoot.WriteTo(writer);
notRoot.WriteTo(writer);
}
<?xml version="1.0" encoding="utf-16"?><root><notRoot><id>1</id></notRoot><notRoot><id>1</id></notRoot></root>
Note that I'm omitting the End calls, since the XmlWriter will take care of that implicitly.
If you aren't doing anything interesting with the xmlSettings, the whole thing is even simpler since XElement.Save has an overload that accepts a file name directly, so you don't need an XmlWriter at all.

Related

Token StartElement in state Epilog would result in an invalid XML document

I am getting the error "Token StartElement in state Epilog would result in an invalid XML document." when i get the data from datatable and try to convery it to xml file .
Code :
DataTable dtTest = new DataTable();
dtTest.Columns.Add("Name");
dtTest.Columns.Add("NickName");
dtTest.Columns.Add("Code");
dtTest.Columns.Add("reference");
dtTest.Rows.Add("Yash", "POPs", "Vapi", "None1");
dtTest.Rows.Add("shilpa", "shilpa", "valsad", "None2");
dtTest.Rows.Add("Dinesh", "dinu", "pune", "None3");
dtTest.Rows.Add("rahul", "mady", "pardi", "None4");
XmlWriterSettings settings = new XmlWriterSettings();
StringWriter stringwriter = new StringWriter();
XmlTextWriter xmlTextWriter = new XmlTextWriter(stringwriter);
xmlTextWriter.Formatting = Formatting.Indented;
xmlTextWriter.WriteStartDocument();
foreach (var row in dtTest.AsEnumerable())
{
xmlTextWriter.WriteStartElement("");
xmlTextWriter.WriteAttributeString("orderid", row.Field<string>("Name"));
xmlTextWriter.WriteElementString("type", row.Field<string>("Name"));
xmlTextWriter.WriteElementString("status", row.Field<string>("Name"));
xmlTextWriter.WriteElementString("productno", row.Field<string>("Name"));
xmlTextWriter.WriteEndElement();
}
XmlDocument docSave = new XmlDocument();
docSave.LoadXml(stringwriter.ToString());
What is the cause of this error, and how can it be fixed?
You have a few problems here:
You are writing multiple root elements to your document, one for each call to xmlTextWriter.WriteStartElement(""). However, a well-formed XML document must have one and only one root element, so you need to wrap your row elements in some container element.
(You might have been thinking that WriteStartDocument() would write the root element, but it does not. It just writes the XML declaration.)
You are using XmlTextWriter but this class is deprecated. From the docs:
Starting with the .NET Framework 2.0, we recommend that you create XmlWriter instances by using the XmlWriter.Create method and the XmlWriterSettings class to take advantage of new functionality.
If you switch to XmlWriter you will get clearer error messages and better error checking.
You are trying to write elements with no name:
xmlTextWriter.WriteStartElement("");
This would result in malformed XML. XmlTextWriter seems not to check for this, but XmlWriter does.
Putting all of the above together, your code can be modified as follows:
var stringwriter = new StringWriter();
using (var xmlWriter = XmlWriter.Create(stringwriter, new XmlWriterSettings { Indent = true }))
{
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("Root");
foreach (var row in dtTest.AsEnumerable())
{
xmlWriter.WriteStartElement("Row");
xmlWriter.WriteAttributeString("orderid", row.Field<string>("Name"));
xmlWriter.WriteElementString("type", row.Field<string>("Name"));
xmlWriter.WriteElementString("status", row.Field<string>("Name"));
xmlWriter.WriteElementString("productno", row.Field<string>("Name"));
xmlWriter.WriteEndElement();
}
xmlWriter.WriteEndElement();
}
var xml = stringwriter.ToString();
Demo fiddle here.

Remove root node and first child from XML document, while keeping second child node

I am currently developing a custom pipeline component for an XML document flow, where the root node and the first child of that root node needs to be stripped off, leaving only the second child node left (which is now the new root node).
I'm using XDocument as container class for the XML document. Ive written some code which gets the second child node, and creates a new XML document with that node as the root, thus removing the two undesired nodes from the picture.
XNode secondChild = xDoc.Root.Elements().First().NextNode;
XDocument outputXml = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
secondChild);
But when I test this setup in Biztalk, I only get an empty document back as a response. It seems to create an empty XML document which is then returned.
To give an example of what I want to achieve:
I want to go from a structure like this:
<Root>
<FirstChild></FirstChild>
<SecondChild></SecondChild>
</Root>
To a simple structure like this:
<SecondChild></SecondChild>
The full code of the Execute method in the pipeline:
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
var originalStream = pInMsg.BodyPart.GetOriginalDataStream();
XDocument xDoc; //new XML document to return as the message
using (XmlReader reader = XmlReader.Create(originalStream))
{
reader.MoveToContent();
xDoc = XDocument.Load(reader);
}
XNode secondChild = xDoc.Root.Elements().First().NextNode;
XDocument outputXml = new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
secondChild);
// Returning stream, serializing the XML to byte array
byte[] output = System.Text.Encoding.ASCII.GetBytes(outputXml.ToString());
MemoryStream memoryStream = new MemoryStream();
memoryStream.Write(output, 0, output.Length);
memoryStream.Position = 0;
pInMsg.BodyPart.Data = memoryStream; //overwrite the original message with the modified stream
return pInMsg;
}
Looking around on SO I found this answer, which I tried to follow, but as mentioned it produces an empty document. Is there a different option, other than simply creating a new XDocument?
If you develop a regular map to edit the document you can place it in the maps section of the receive port. It's simpler to create, test, and install than a pipeline component.

Linq to Xml: error on saving a document twice

I have the following xml:
<?xml version="1.0" encoding="utf-16" standalone="yes"?>
<property_set_list xmlns="myNamespace">
<property_set symbol_id="Config">
</property_set>
</property_set_list>
I want to open it, add some property then close it and edit the added property and then save again:
var xws = new XmlWriterSettings { Indent = true, IndentChars = TAB };
using (var reader = ReaderCreator())
using (var output = OutputCreator())
using (var xmlWriter = XmlWriter.Create(output, xws))
{
XDoc = XElement.Load(reader, LoadOptions.None);
Namespace = "myNamespace";
// Append node
AppendToNode("Config", "", MAIN_LOBBY_LIST_CTRL_LOCAL_TABLES_COLOR,
318, 8);
XDoc.Save(xmlWriter);
// Edit added node
SetColors();
// Error here
XDoc.Save(xmlWriter);
}
The property is added. Then saved successfully. Edited Successfully. But I receive the following error on the second save :
"Token StartDocument in state End Document would result in an invalid XML document."
What can I do here ? Any suggestions are welcome.
You have to create another XmlWriter. There is no way to reset it and start writing again from the begining of underlying stream.

Overwriting root element in syndicationfeed, Adding namespaces to root element

I need to add new namespaces to the rss(root) element of my feed, in addition to a10:
<rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0">
<channel>
.
.
.
I am using a SyndicationFeed class serialized to RSS 2.0 and I use a XmlWriter to output the feed,
var feed = new SyndicationFeed(
feedDefinition.Title,
feedDefinition.Description,
.
.
.
using (var writer = XmlWriter.Create(context.HttpContext.Response.Output, settings))
{
rssFormatter.WriteTo(writer);
}
I have tried adding AttributeExtensions on SyndicationFeed but it adds the new namespaces
to the channel element instead of the root,
Thank you
Unfortunately the formatter is not extensible in the way you need.
You can use an intermediate XmlDocument and modify it before writing to the final output.
This code will add a namespace to the root element of the final xml output:
var feed = new SyndicationFeed("foo", "bar", new Uri("http://www.example.com"));
var rssFeedFormatter = new Rss20FeedFormatter(feed);
// Create a new XmlDocument in order to modify the root element
var xmlDoc = new XmlDocument();
// Write the RSS formatted feed directly into the xml doc
using(var xw = xmlDoc.CreateNavigator().AppendChild() )
{
rssFeedFormatter.WriteTo(xw);
}
// modify the document as you want
xmlDoc.DocumentElement.SetAttribute("xmlns:example", "www.example.com");
// now create your writer and output to it:
var sb = new StringBuilder();
using (XmlWriter writer = XmlWriter.Create(sb))
{
xmlDoc.WriteTo(writer);
}
Console.WriteLine(sb.ToString());

Creating an XML with multiple root elements

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 />"

Categories

Resources