i have some XML files and i have wrote a c# application to check missing elements, nodes and save it back. In my XMLs attributes are use single quotes (ex: <Person name='Nisala' age='25' >). But when saving C# application convert those quotes to double quotes. Then i found following code to save using single quotes
using (XmlTextWriter tw = new XmlTextWriter(file, null))
{
tw.Formatting = Formatting.Indented;
tw.Indentation = 3;
tw.IndentChar = ' ';
tw.QuoteChar = '\'';
xmlDoc.Save(tw);
}
}
but it will append XML declaration there. then i found this code to remove xml declaration
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = true;
xws.ConformanceLevel = ConformanceLevel.Fragment;using (XmlWriter xw = XmlWriter.Create(file, xws)){
xmlDoc.Save(xw);
}
then again XML declaration is appending to text. How can i use both of them?
i have tried following code too, but no use of it
XmlWriterSettings xws = new XmlWriterSettings();
xws.OmitXmlDeclaration = true;
xws.Indent = true;
xws.ConformanceLevel = ConformanceLevel.Fragment;
using (XmlTextWriter tw = new XmlTextWriter(file, null))
{
tw.Formatting = Formatting.Indented;
tw.Indentation = 3;
tw.IndentChar = ' ';
tw.QuoteChar = '\'';
using (XmlWriter xw = XmlWriter.Create(tw, xws))
{
xmlDoc.Save(xw);
}
}
The XML declaration is written by calling WriteStartDocument on the XmlWriter implementation. The behaviour of this is can be altered when you use the recommended XmlWriter.Create with XmlWriterSettings.
However, the recommended method doesn't allow you to change the quote character.
The only solution I can think of is to create your own writer, deriving from XmlTextWriter. You would then override WriteStartDocument to prevent any declaration being written:
public class XmlTextWriterWithoutDeclaration : XmlTextWriter
{
public XmlTextWriterWithoutDeclaration(Stream w, Encoding encoding)
: base(w, encoding)
{
}
public XmlTextWriterWithoutDeclaration(string filename, Encoding encoding)
: base(filename, encoding)
{
}
public XmlTextWriterWithoutDeclaration(TextWriter w)
: base(w)
{
}
public override void WriteStartDocument()
{
}
}
And use as you are now:
using (var tw = new XmlTextWriterWithoutDeclaration(file, null))
{
tw.Formatting = Formatting.Indented;
tw.Indentation = 3;
tw.IndentChar = ' ';
tw.QuoteChar = '\'';
xmlDoc.Save(tw);
}
Related
Here, i created a class, and what i am trying to accomplish is to write the contents from the list into an xml file.
1) At first run, it creates the file and trows an error here: Token EndElement in state EndRootElement would result in an invalid XML document
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//second run, error Occurr here
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//first Run error is thrown in here.
xmlOut.WriteEndElement();
xmlOut.Close();
}
2) When i rerun on the second time, the error is in same method.
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//xml writer object, CellPhoneProduct
XmlWriter xmlOut = XmlWriter.Create(path, localSettings);
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
xmlOut.WriteEndElement();
xmlOut.Close();
}
The whole class i here:
class ProductCellPhoneDB
{
private const string path = #"..\..\CellPhoneProducts.xml";
public static List<ProducCellPhone> GetCellPhoneProducts()
{
List<ProducCellPhone> localCellPhoneProducts =
new List<ProducCellPhone>();
XmlReaderSettings localSettings = new XmlReaderSettings();
localSettings.IgnoreWhitespace = true;
localSettings.IgnoreComments = true;
XmlReader xmlIn = (XmlReader.Create(path,localSettings));
if (xmlIn.ReadToDescendant("Cell"))
{
do
{
ProducCellPhone localProduct = null;
xmlIn.ReadStartElement("Cell");
localCellPhoneProducts.Add(localProduct);
}
while (xmlIn.ReadToNextSibling("Cell"));
}
xmlIn.Close();
return localCellPhoneProducts;
}
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
//Error Occurr here
//xml writer object, CellPhoneProduct, error is being used by other process?
XmlWriter xmlOut = (XmlWriter.Create(path, localSettings));
xmlOut.WriteStartDocument();
xmlOut.WriteStartElement("Cell");
foreach(ProducCellPhone localProduct in LocalProducts)
{
WriteCellPhoneProductBase(localProduct, xmlOut);
}
//ERROR Token EndElement in state EndRootElement would result in an invalid XML document
xmlOut.WriteEndElement();
xmlOut.Close();
}
private static void ReadCellphoneProductBase(XmlReader xmlIn, ProducCellPhone localProduct)
{
localProduct.Iemi = xmlIn.ReadElementContentAsString();
localProduct.Model = xmlIn.ReadContentAsString();
localProduct.Price = xmlIn.ReadContentAsDecimal();
}
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
}
Any suggestions would be helpful. Thanks community. !
The first error
you get is likely because the WriteStartElement and WriteEndElement calls are not matched. You do xmlOut.WriteStartElement("Cell"); once, but do xmlout.WriteEndElement(); several times, once for each ProducCellPhone in LocalProducts, plus another time after the foreach.
To solve this (if I guessed your XML document structure right), you should change your WriteCellPhoneProductBase method to:
private static void WriteCellPhoneProductBase(ProducCellPhone localProduct,
XmlWriter xmlout)
{
xmlOut.WriteStartElement("Cell");
xmlout.WriteElementString("IEMI", localProduct.Iemi);
xmlout.WriteElementString("Model", localProduct.Model);
xmlout.WriteElementString("Price", Convert.ToString(localProduct.Price));
xmlout.WriteEndElement();
}
And remove the WriteStartElement and WriteEndElement lines from SaveCellPhoneProducts (see below).
The second error is probably because the XmlWriter used when you got the first error was not disposed and has not closed the file handle. You should always use a using block to ensure IDisposable resources get disposed, also when an exception occurs. You should change your method to:
public static void SaveCellPhoneProducts(List<ProducCellPhone> LocalProducts)
{
//xml writer settings
XmlWriterSettings localSettings = new XmlWriterSettings();
localSettings.Indent = true;
localSettings.IndentChars = (" ");
using (var xmlOut = XmlWriter.Create(path, localSettings))
{
xmlOut.WriteStartDocument();
//write each product on xml file
foreach(ProducCellPhone localProduct in LocalProducts)
WriteCellPhoneProductBase(localProduct, xmlOut);
xmlOut.WriteEndDocument()
}
}
For your GetCellPhoneProducts follow the same using block approach.
I am looking on Internet how keep the carriage return from XML data but I could not find the answer, so I'm here :)
The objective is to write in a file the content of a XML data. So, if the value of the node contains some "\r\n" data, the soft need to write them in file in order to create new line, but it doesn't write, even with space:preserve.
Here is my test class:
XElement xRootNode = new XElement("DOCS");
XElement xData = null;
//XNamespace ns = XNamespace.Xml;
//XAttribute spacePreserve = new XAttribute(ns+"space", "preserve");
//xRootNode.Add(spacePreserve);
xData = new XElement("DOC");
xData.Add(new XAttribute("FILENAME", "c:\\titi\\prout.txt"));
xData.Add(new XAttribute("MODE", "APPEND"));
xData.Add("Hi my name is Baptiste\r\nI'm a lazy boy");
xRootNode.Add(xData);
bool result = Tools.writeToFile(xRootNode.ToString());
And here is my process class:
try
{
XElement xRootNode = XElement.Parse(xmlInputFiles);
String filename = xRootNode.Element(xNodeDoc).Attribute(xAttributeFilename).Value.ToString();
Boolean mode = false;
try
{
mode = xRootNode.Element(xNodeDoc).Attribute(xWriteMode).Value.ToString().ToUpper().Equals(xWriteModeAppend);
}
catch (Exception e)
{
mode = false;
}
String value = xRootNode.Element(xNodeDoc).Value;
StreamWriter destFile = new StreamWriter(filename, mode, System.Text.Encoding.Unicode);
destFile.Write(value);
destFile.Close();
return true;
}
catch (Exception e)
{
return false;
}
Does anybody have an idea?
If you want to preserve cr lf in element or attribute content when saving a XDocument or XElement you can do that by using certain XmlWriterSettings, namely NewLineHandling to Entitize:
string fileName = "XmlOuputTest1.xml";
string attValue = "Line1.\r\nLine2.";
string elementValue = "Line1.\r\nLine2.\r\nLine3.";
XmlWriterSettings xws = new XmlWriterSettings();
xws.NewLineHandling = NewLineHandling.Entitize;
XDocument doc = new XDocument(new XElement("root",
new XAttribute("test", attValue),
elementValue));
using (XmlWriter xw = XmlWriter.Create(fileName, xws))
{
doc.Save(xw);
}
doc = XDocument.Load(fileName);
Console.WriteLine("att value: {0}; element value: {1}.",
attValue == doc.Root.Attribute("test").Value,
elementValue == doc.Root.Value);
In that example the value are preserved in the round trip of saving and loading as the output of the sample is "att value: True; element value: True."
Heres a useful link I found for parsing an Xml string with carraige returns, line feeds in it.
howto-correctly-parse-using-xelementparse-for-strings-that-contain-newline-character-in
It may help those who are parsing an Xml string.
For those who can't be bothered to click it says use an XmlTextReader instead
XmlTextReader xtr = new XmlTextReader(new StringReader(xml));
XElement items = XElement.Load(xtr);
foreach (string desc in items.Elements("Item").Select(i => (string)i.Attribute("Description")))
{
Console.WriteLine("|{0}|", desc);
}
My object template, which is deserialized from a hand made XML file contains mixed types and the text can contain line jumps. When I look at the text I can see line jumps are \r\n, but in my deserialized template object, line jumps are \n. How can I keep line jumps as \r\n?
XmlReaderSettings settings = new XmlReaderSettings();
settings.CloseInput = true;
//settings.ValidationEventHandler += ValidationEventHandler;
settings.ValidationType = ValidationType.Schema;
settings.Schemas.Add(schema);
StringReader r = new StringReader(syntaxEdit.Text);
Schema.template rawTemplate = null;
using (XmlReader validatingReader = XmlReader.Create(r, settings))
{
try
{
XmlSerializer serializer = new XmlSerializer(typeof(Schema.template));
rawTemplate = serializer.Deserialize(validatingReader) as Schema.template;
}
catch (Exception ex)
{
rawTemplate = null;
string floro = ex.Message + (null != ex.InnerException ? ":\n" + ex.InnerException.Message : "");
MessageBox.Show(floro);
}
}
It seems that this is required behavior by the XML specification and is a "feature" in Microsoft's implementation of the XmlReader (see this answer).
Probably the easiest thing for you to do would be to replace \n with \r\n in your result.
That's the behavior mandated by the XML specification: every \r\n, \r or \n MUST be interpreted as a single \n character. If you want to maintain the \r in your output, you have to change it to a character reference (
) as shown below.
public class StackOverflow_7374609
{
[XmlRoot(ElementName = "MyType", Namespace = "")]
public class MyType
{
[XmlText]
public string Value;
}
static void PrintChars(string str)
{
string toEscape = "\r\n\t\b";
string escapeChar = "rntb";
foreach (char c in str)
{
if (' ' <= c && c <= '~')
{
Console.WriteLine(c);
}
else
{
int escapeIndex = toEscape.IndexOf(c);
if (escapeIndex >= 0)
{
Console.WriteLine("\\{0}", escapeChar[escapeIndex]);
}
else
{
Console.WriteLine("\\u{0:X4}", (int)c);
}
}
}
Console.WriteLine();
}
public static void Test()
{
string serialized = "<MyType>Hello\r\nworld</MyType>";
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
XmlSerializer xs = new XmlSerializer(typeof(MyType));
MyType obj = (MyType)xs.Deserialize(ms);
Console.WriteLine("Without the replacement");
PrintChars(obj.Value);
serialized = serialized.Replace("\r", "
");
ms = new MemoryStream(Encoding.UTF8.GetBytes(serialized));
obj = (MyType)xs.Deserialize(ms);
Console.WriteLine("With the replacement");
PrintChars(obj.Value);
}
}
I am serializing objects to XML with the following code:
public static string SerializeToString<T>(T objectToBeSerialized, string defaultNamespace)
{
StringBuilder stringBuilder = new StringBuilder();
XmlWriterSettings xmlSettings = new XmlWriterSettings()
{
CloseOutput = true,
Indent = true,
OmitXmlDeclaration = true
};
using (XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, xmlSettings))
{
XmlSerializer serializer = new XmlSerializer(typeof(T), defaultNamespace);
serializer.Serialize(xmlWriter, objectToBeSerialized);
return stringBuilder.ToString();
}
}
I am already setting the default namespace ("http://schemas.somecompany.com/online/someservice/sync/2008/11"); however, my outputs still contain the default "xmlns:xsi" and "xmlns:xsd
<RootTag ***xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"*** xmlns="http://schemas.somecompany.com/online/someservice/sync/2008/11">
<SomeTag>
<More>false</More>
</SomeTag>
</RootTage>
How can I get rid of them?
XmlSerializer: remove unnecessary xsi and xsd namespaces
I'm using XmlTextWriter and its WriteElementString method, for example:
XmlTextWriter writer = new XmlTextWriter("filename.xml", null);
writer.WriteStartElement("User");
writer.WriteElementString("Username", inputUserName);
writer.WriteElementString("Email", inputEmail);
writer.WriteEndElement();
writer.Close();
The expected XML output is:
<User>
<Username>value</Username>
<Email>value</Email>
</User>
However, if for example inputEmail is empty, the result XML I get as as follows:
<User>
<Username>value</Username>
<Email/>
</User>
Whereas I would expect it to be:
<User>
<Username>value</Username>
<Email></Email>
</User>
What am I doing wrong? Is there a way to achieve my expected result in a simple way using XmlTextWriter?
Your output is correct. An element with no content should be written as <tag/>.
You can force the use of the full tag by calling WriteFullEndElement()
writer.WriteStartElement("Email");
writer.WriteString(inputEmail);
writer.WriteFullEndElement();
That will output <Email></Email> when inputEmail is empty.
If you want to do that more than once, you could create an extension method:
public static void WriteFullElementString(this XmlTextWriter writer,
string localName,
string value)
{
writer.WriteStartElement(localName);
writer.WriteString(value);
writer.WriteFullEndElement();
}
Then your code would become:
writer.WriteStartElement("User");
writer.WriteFullElementString("Username", inputUserName);
writer.WriteFullElementString("Email", inputEmail);
writer.WriteEndElement();
It doesn't fail <Tag/> is just a shortcut for <Tag></Tag>
Your code should be:
using (XmlWriter writer = XmlWriter.Create("filename.xml"))
{
writer.WriteStartElement("User");
writer.WriteElementString("Username", inputUserName);
writer.WriteElementString("Email", inputEmail);
writer.WriteEndElement();
}
This avoids resource leaks in case of exceptions, and uses the proper way to create an XmlReader (since .NET 2.0).
Leaving this here in case someone needs it; since none of the answers above solved it for me, or seemed like overkill.
FileStream fs = new FileStream("file.xml", FileMode.Create);
XmlWriterSettings settings = new XmlWriterSettings();
settings.Indent = true;
XmlWriter w = XmlWriter.Create(fs, settings);
w.WriteStartDocument();
w.WriteStartElement("tag1");
w.WriteStartElement("tag2");
w.WriteAttributeString("attr1", "val1");
w.WriteAttributeString("attr2", "val2");
w.WriteFullEndElement();
w.WriteEndElement();
w.WriteEndDocument();
w.Flush();
fs.Close();
The trick was to set the XmlWriterSettings.Indent = true and add it to the XmlWriter.
Edit:
Alternatively you can also use
w.Formatting = Formatting.Indented;
instead of adding an XmlWriterSettings.
Tried solving this with another approach, might need optimization.
public class SerializeConfig<T> where T : class
{
public static string Serialize(T type)
{
var settings = new XmlWriterSettings
{
Encoding = Encoding.UTF8,
Indent = true,
OmitXmlDeclaration = true
};
var sb = new StringBuilder();
var serializer = new XmlSerializer(type.GetType());
using (var writer = XmlWriter.Create(sb, settings))
{
serializer.Serialize(writer, type);
}
return sb.ToString().FixXmlClosingTags();
}
}
internal static class InsertStringExtention
{
public static string FixXmlClosingTags(this string xmlString)
{
var sb = new StringBuilder();
var xmlTags = xmlString.Split('\r');
foreach (var tag in xmlTags)
{
if (tag.Contains("/>"))
{
var tagValue = tag.Replace("<", "").Replace("/>", "").Trim();
var firstPart = tag.Substring(0, tag.IndexOf('<'));
var newTag = $"{firstPart}<{tagValue}></{tagValue}>";
sb.Append(newTag);
}
else
{
sb.Append(tag);
}
}
return sb.ToString();
}
}