Linq to Xml: error on saving a document twice - c#

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.

Related

Converting XML/XSLT to HTML

I have the following XML that came from TNT that I should be able to use to create a shipping label:
https://codebeautify.org/xmlviewer/cb55b98e
I have been supplied with the following XSL file:
https://express.tnt.com/expresswebservices-website/stylesheets/HTMLAddressLabelRenderer.xsl
I have attempted to combine them with the following code:
XmlWriterSettings settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
ConformanceLevel = ConformanceLevel.Fragment,
CloseOutput = false,
};
// populate the root element with the XML of the address label
XElement root = new XElement("root", XElement.Parse(await _engine.GetDocument("GET_LABEL", code)));
XDocument newTree = new XDocument();
using (XmlWriter writer = XmlWriter.Create(newTree.CreateWriter(), settings))
{
XslCompiledTransform xslt = new XslCompiledTransform();
XsltSettings trev = new XsltSettings
{
EnableDocumentFunction = true,
EnableScript = true
};
xslt.Load(#"C:\Users\Trevo\Desktop\HTMLAddressLabelRenderer.xsl", trev, null);
xslt.Transform(root.CreateReader(), writer);
writer.Close();
newTree.Save(#"C:\Users\Trevo\Desktop\result.html");
}
the HTML only contains the script and head properties and the body is completely empty.
I cannot work out why it isn't working. I have considered that the "root" is not the correct XName but unsure how to work out what it should be.
Any help would be greatly appreciated!
Try this freeformatter
For XML Input, on the first line, enter the below xml, then paste your xml code
<?xml version="1.0"?>
For XML Output, just paste your xsl code.
Click Transform XML. Just play around with the formatter but it will give html output.

C# - Append an XElement array to XElement

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.

Correct way to add a namespace to an element using XmlDocument

I have to modify a incoming SOAP XML message to add a namespace to one of the elements so that the deserialisation will work. However when I add the xmlns attribute to the correct element, I get an error when I try to load the xml in to an XmlWriter via a stream (which I need to do to in my IClientMessageInspector implementation to replace the Message reply).
The prefix '' cannot be redefined from '' to 'http://www.example.com' within the same start element tag.
I have a work around, which is that after I've modifed the attribute, I reload the entire XML document from it's own OuterXML. This works for some reason, but makes me think there must be a 'correct' way to do this.
Here's a sample test that demonstrates the problem and my current solutions:
[Test]
public void XmlNamespaceTest()
{
var originalXmlString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><TestElement><Child>thing</Child></TestElement>";
var desiredXmlString = "<?xml version=\"1.0\" encoding=\"utf-8\"?><TestElement xmlns=\"http://www.example.com\"><Child>thing</Child></TestElement>";
var doc = new XmlDocument();
doc.LoadXml(originalXmlString);
Assert.That(originalXmlString, Is.EqualTo(doc.OuterXml));
// Write this document via an XMLWriter
var ms = new MemoryStream();
var xmlWriter = XmlWriter.Create(ms);
doc.WriteTo(xmlWriter);
xmlWriter.Flush();
ms.Position = 0;
StreamReader sr = new StreamReader(ms);
var originalXmlViaXmlWriter = sr.ReadToEnd();
Assert.That(originalXmlString, Is.EqualTo(originalXmlViaXmlWriter));
// Add the namespace to the element
((XmlElement)doc.GetElementsByTagName("TestElement").Item(0))?.SetAttribute("xmlns", "http://www.example.com");
Assert.That(desiredXmlString, Is.EqualTo(doc.OuterXml));
// Now attempt to write this modified xml via an XMLWriter
ms.SetLength(0);
xmlWriter = XmlWriter.Create(ms);
//Uncomment line below to fix the error.
//doc.LoadXml(doc.OuterXml);
doc.WriteTo(xmlWriter);
xmlWriter.Flush();
ms.Position = 0;
sr = new StreamReader(ms);
var modifedXmlViaXmlWriter = sr.ReadToEnd();
Assert.That(desiredXmlString, Is.EqualTo(modifedXmlViaXmlWriter));
}
According to this you can't change an elements namespace in an XmlDocument. This is also what #LocEngineer found in his comment. The referenced article mentions that the only way to do this is to save and reload the XmlDocument, which is exactly what you are doing.
If you are in a position to use XDoxument instead, it is possible. See this answer for a solution.

Error: The XML declaration must be the first node in the document

I am getting "Unexpected XML declaration. The XML declaration must be the first node in the document, and no white space characters are allowed to appear before it" error while trying to load xml. Both my C# code and contents of XML file are given below. XML definition exists in Line 6 of the xml file and hence the error.
I can not control what's there in the xml file so how can I edit/rewrite it using C# such that xml declaration comes first and then the comments to load it without any error!
//xmlFilepath is the path/name of the xml file passed to this function
static function(string xmlFilepath)
{
XmlReaderSettings readerSettings = new XmlReaderSettings();
readerSettings.IgnoreComments = true;
readerSettings.IgnoreWhitespace = true;
XmlReader reader = XmlReader.Create(XmlFilePath, readerSettings);
XmlDocument xml = new XmlDocument();
xml.Load(reader);
}
XmlDoc.xml
<!-- Customer ID: 1 -->
<!-- Import file: XmlDoc.xml -->
<!-- Start time: 8/14/12 3:15 AM -->
<!-- End time: 8/14/12 3:18 AM -->
<?xml version="1.0" encoding="ISO-8859-1" standalone="yes"?>
-----
As the error states, the first five characters of an XML document should be <?xml. No ifs, ands or buts. The comments you have above the opening XML tag are illegal; they must go inside the XML tag (because the comment structure is itself defined by the XML standard and so is meaningless outside the main XML tags).
EDIT: Something like this should be able to rearrange the rows, given the file format from the OP:
var lines = new List<string>();
using (var fileStream = File.Open(xmlFilePath, FileMode.Open, FileAccess.Read))
using(var reader = new TextReader(fileStream))
{
string line;
while((line = reader.ReadLine()) != null)
lines.Add(line);
}
var i = lines.FindIndex(s=>s.StartsWith("<?xml"));
var xmlLine = lines[i];
lines.RemoveAt(i);
lines.Insert(0,xmlLine);
using (var fileStream = File.Open(xmlFilePath, FileMode.Truncate, FileAccess.Write)
using(var writer = new TextWriter(fileStream))
{
foreach(var line in lines)
writer.Write(line);
writer.Flush();
}
That is not valid XML.
As the error clearly states, the XML declaration (<?xml ... ?>) must come first.
I'm using the following function to remove whitespace from xml:
public static void DoRemovespace(string strFile)
{
string str = System.IO.File.ReadAllText(strFile);
str = str.Replace("\n", "");
str = str.Replace("\r", "");
Regex regex = new Regex(#">\s*<");
string cleanedXml = regex.Replace(str, "><");
System.IO.File.WriteAllText(strFile, cleanedXml);
}
Don't put any comments in the beginning of your file!

Deserialization error in XML document(1,1)

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());
}

Categories

Resources