XDocument is removing document comment - c#

When I run <!-- This list contains names/words with specific casing - and specific to english only --> will be removed in new destination.
Is there any way to prevent it from happening. I event tried with XDocument.Root same thing happened.
Ps: I know method are not inside class :).
<!-- This list contains names/words with specific casing - and specific to english only -->
<ignore_list>
<name>A.M.</name>
<name>Aaron</name>
<name>Abbie</name>
<name>Abi</name>
<name>Abigail</name>
<name>Abolfazl</name>
<name>Adam</name>
</ignore_list>
private static void EnGBNamesEtc()
{
var listNames = new List<string>(){"some", "names");
var xele = XElement.Load(#"C:\Users\ivandro\Source\subtitleedit\Dictionaries\en_GB_names_etc.xml");
var names = listNames.Except(xele.Elements("name").Select(n => n.Value)).ToList();
foreach (var newName in names)
{
xele.Add(new XElement("name", newName));
}
SaveToNewXml(xele, #"D:\Backup\en_GB_names_etc1.xml");
}
private static void SaveToNewXml(XElement xelem, string dest)
{
XmlWriterSettings xws = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true };
var ordered = xelem.Elements("name").OrderBy(element => element.Value).ToList();
using (XmlWriter xw = XmlWriter.Create(dest, xws))
{
xelem.ReplaceAll(ordered);
xelem.Save(xw);
}
}

That's possibly because you're using XElement which only capable of representing single node (when you have two "root" nodes, <ignore_list> and the comment node). Use XDocument instead of XElement to keep the comment node, for example (I tried to keep minimal changes to your original code) :
private static async void EnGBNamesEtc()
{
var listNames = new List<string>(){"some", "names");
var xdoc = XDocument.Load(#"C:\Users\ivandro\Source\subtitleedit\Dictionaries\en_GB_names_etc.xml");
var names = listNames.Except(xdoc.Root.Elements("name").Select(n => n.Value)).ToList();
foreach (var newName in names)
{
xdoc.Root.Add(new XElement("name", newName));
}
SaveToNewXml(xdoc, #"D:\Backup\en_GB_names_etc1.xml");
}
private static void SaveToNewXml(XDocument xdoc, string dest)
{
XmlWriterSettings xws = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = true };
var ordered = xdoc.Root.Elements("name").OrderBy(element => element.Value).ToList();
using (XmlWriter xw = XmlWriter.Create(dest, xws))
{
xdoc.Root.ReplaceAll(ordered);
xdoc.Save(xw);
}
}

Related

How to: preview and print in my c# class library?

I wrote a c# class that generate a html file and return this a string.
But I need to add a preview this document HTML (it can be in the browser), and print out option this document.
My code:
public class Class1
{
public string HTMGenerator()
{
string html = "<p>test</p>";
var xDocument = new XDocument(
new XDocumentType("html", null, null, null),
new XElement("html",
new XElement("head"),
new XElement("body"
XElement.Parse(html)),
)
);
var settings = new XmlWriterSettings
{
OmitXmlDeclaration = true,
Indent = true,
IndentChars = "\t"
};
using (var sw = new StringWriter())
{
using (var writer = XmlWriter.Create(sw, settings))
{
xDocument.WriteTo(writer);
}
return sw.ToString();
}
}
}
Try this:
string filename = "Your FileName.html";
File.WriteAllText(filename, HTMGenerator());
Process.Start(filename);
Here's how I would write your code:
void Main()
{
var filename ="Your File.html";
var instance = new Class1();
var document = instance.HTMGenerator();
document.Save(filename);
Process.Start(filename);
}
public class Class1
{
public XDocument HTMGenerator()
{
string html = "<p>test</p>";
var xDocument =
new XDocument(
new XDocumentType("html", null, null, null),
new XElement(
"html",
new XElement("head"),
new XElement(
"body",
XElement.Parse(html))));
return xDocument;
}
}

C#: How to shorten that XML appending?

I have that XML file which looks like this:
<?xml version="1.0" encoding="utf-8"?>
<backupatmail>
<backup id="0">
<foldername>TestFolder</foldername>
<backupdate>09/10/2015</backupdate>
<comment>Sample comment text is here</comment>
<numberofparts>7</numberofparts>
<lastsucceed>Test.007</lastsucceed>
</backup>
<backup id="1">
<foldername>TestFolder2</foldername>
<backupdate>09/10/2015</backupdate>
<comment>Sample comment text is here</comment>
<numberofparts>15</numberofparts>
<lastsucceed>Test.015</lastsucceed>
</backup>
</backupatmail>
Now, I want to append new node(?):
<backup id="999">
<foldername>testing1</foldername>
<backupdate>99/99/9999</backupdate>
</backup>
I wrote following code:
public static void AddBackupToXML()
{
XmlDocument doc = new XmlDocument();
doc.Load(GlobalSettings.appDefaultFolder + "backups.xml");
XmlElement backupNodeNew = doc.CreateElement("backup");
XmlAttribute backupId = doc.CreateAttribute("id");
backupId.Value = "999";
backupNodeNew.Attributes.Append(backupId);
XmlNode nodeTitle = doc.CreateElement("foldername");
nodeTitle.InnerText = "testing1";
XmlNode nodeUrl = doc.CreateElement("backupdate");
nodeUrl.InnerText = "99/99/9999";
backupNodeNew.AppendChild(nodeTitle);
backupNodeNew.AppendChild(nodeUrl);
doc.DocumentElement.AppendChild(backupNodeNew);
doc.Save(GlobalSettings.appDefaultFolder + "backups.xml");
}
Is there any shorten way to do that and also keep simplicity at beginners level?
I will recommend you to use LINQ to XML. It has more simple API for working with XML and your code will look like
var file_name = GlobalSettings.appDefaultFolder + "backups.xml";
XDocument xdoc = XDocument.Load(file_name);
var backup999 = new XElement("backup",
new XAttribute("id", 999),
new XElement("foldername", "testing1"),
new XElement("backupdate", "99/99/9999")
);
xdoc.Root.Add(backup999);
xdoc.Save(file_name);
How I did it plenty of times, using copy special feature of visual studio http://blog.codeinside.eu/2014/09/08/Visual-Studio-2013-Paste-Special-JSON-And-Xml/ I generated the classed describing the xml structure, handle it really clean and easy from code and transfomed when required with System.Xml.Serialization https://support.microsoft.com/en-us/kb/815813 in xml doc or whatever was the need.
The same way from xml to c# object https://msdn.microsoft.com/en-us/library/fa420a9y(v=vs.110).aspx.
My personal preference is to not use the xml api directly. Rather, use the model object then serialize and de-serialize it. It's much cleaner in my opinion and the code to do what you actually need is only 3 lines once your have the generic serializer in place.
What you can do is de-serialize your object into an actual "backupatmail" object and then call backupatmail.BackUpItems.Add(your new item here) and then deserialize it back into a string.
This approach is slower than using LinqToXml during runtime but most of the time a extra millisecond or more isn't a big deal.
Here's the serialization/ deserialization code.
using System.Xml;
using System.Xml.Serialization;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
public class SerializationUtils
{
public static T Deserialize<T>(string data)
{
XmlSerializer objSerializer = new XmlSerializer(typeof(T));
using (var reader = new StringReader(data))
{
return (T)objSerializer.Deserialize(reader);
}
}
public static string Serialize<T>(T obj)
{
XmlSerializer objSerializer = new XmlSerializer(typeof(T));
XmlSerializerNamespaces emptyNamespaces = new XmlSerializerNamespaces(new [] { XmlQualifiedName.Empty });
var settings = new XmlWriterSettings();
settings.OmitXmlDeclaration = true;
settings.ConformanceLevel = ConformanceLevel.Auto;
#if DEBUG
settings.Indent = true;
#else
settings.Indent = false;
#endif
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
objSerializer.Serialize(writer, obj, emptyNamespaces);
return stream.ToString();
}
}
}
and for your use.
string mailItems = GetXmlStringFromSomewhere();
var objMail = SerializationUtils.Deserialize<BackUpMail>(mailItems);
var newItem = new BackUp() { FolderName = "testing1", BackupDate = DateTime.Now};
objMail.BackUpItems.Add(newItem);
var strMailWAddedItem = SerializationUtils.Serialize(objMail);
Here are your entity classes.
public class BackUpMail
{
public List<BackUp> BackUpItems {get;set;}
}
public class BackUp
{
[XmlAttribute("id")]
public string ID {get;set}
[XmlElement("foldername")]
public string FolderName {get;set;}
[XmlElement("backupdate")]
public DateTime BackupDate {get;set;}
[XmlElement("comment")]
public string Comment {get;set}
[XmlElement("numberofparts")]
public string NumberOfParts {get;set}
[XmlElement("lastsucceed")]
public string LastSucceed {get;set}
}
Since you have the code, just parameterize it.
public static void someOtherMethod(){
XmlDocument doc = new XmlDocument();
doc.Load(GlobalSettings.appDefaultFolder + "backups.xml");
// some loop..
doc.DocumentElement.AppendChild(createNode(id[i], foldername[i], backupdate[i]));
doc.Save(GlobalSettings.appDefaultFolder + "backups.xml");
}
public static void createNode(string id, string foldername, string backupdate)
{
XmlElement backupNodeNew = doc.CreateElement("backup");
XmlAttribute backupId = doc.CreateAttribute("id");
backupId.Value = id;
backupNodeNew.Attributes.Append(backupId);
XmlNode nodeTitle = doc.CreateElement("foldername");
nodeTitle.InnerText = foldername;
XmlNode nodeUrl = doc.CreateElement("backupdate");
nodeUrl.InnerText = backupdate;
backupNodeNew.AppendChild(nodeTitle);
backupNodeNew.AppendChild(nodeUrl);
return backupNodeNew;
}

How to serialize List<T> to XML Document.?

I'm not experienced in XML-serialization. I need to serialize System.Collections.Generic.List to XML document. I have the following classes:
public class Person
{
public string Name;
public string Phone;
public Int32 Score;
}
[Serializable()]
public class PersonOperation:Person
{
public String City;
public String OperationType;
public DateTime OperationDate;
public Decimal Amount;
public Decimal AmountEUR;
public void RoubleToEuro(CurrencyCode p_CurrencyCode, Decimal p_CurrencyRate)
{
if (p_CurrencyCode == CurrencyCode.Euro)
{
this.AmountEUR = Decimal.Round(Amount / p_CurrencyRate, 2);
}
}
}
I have a collection of PersonOperation instances that I must serialize to XML.
private List<PersonOperation> _depositorsOperationsList = new List<PersonOperation>();
For XML serialization I try to use the following method:
public XmlDocument GetEntityXml<T>()
{
XmlAttributeOverrides overrides = new XmlAttributeOverrides();
XmlAttributes attr = new XmlAttributes();
attr.XmlRoot = new XmlRootAttribute("Operation");
overrides.Add(typeof(List<T>), attr);
XmlDocument xmlDoc = new XmlDocument();
XPathNavigator nav = xmlDoc.CreateNavigator();
using (XmlWriter writer = nav.AppendChild())
{
XmlSerializer ser = new XmlSerializer(typeof(List<T>), overrides);
ser.Serialize(writer, _depositorsOperationsList);
}
return xmlDoc;
}
I'm in need of the following XML-format after serialization:
<?xml version="1.0" encoding="Windows-1251" ?>
<Operation>
<PersonOperation>
<Name>John Smith</Name>
<Phone>79161234586</Phone>
<City>Glasgow</City>
<Date>2014-02-03</Date>
<OperationType>Join</OperationType>
<Amount>9000.00</Amount>
<AmountEUR>144.06</AmountEUR>
</PersonOperation>
<PersonOperation>
<Name>Bill Satly</Name>
<Phone>79163214569</Phone>
<City>London</City>
<Date>2014-07-10</Date>
<OperationType>Join</OperationType>
<Amount>9000.00</Amount>
<AmountEUR>144.06</AmountEUR>
</PersonOperation>
. . . . . . . . . . .
<Operation>
But instead of this format I have the following one-line horror:
<Operation xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><PersonOperation><Name>John Smith</Name><Phone>79161234586</Phone><Score>270</Score><City>Glasgow</City><OperationType>Join</OperationType><OperationDate>2014-02-03</OperationDate><Amount>9000.0000</Amount><AmountEUR>144.06</AmountEUR></PersonOperation><PersonOperation><Name>Bill Satly</Name><Phone>79163214569</Phone><Score>270</Score><City>London</City><OperationType>Join</OperationType><OperationDate>2014-07-10</OperationDate><Amount>9000.0000</Amount><AmountEUR>144.06</AmountEUR></PersonOperation></Operation>
How can I repair my GetEntityXml method for correct XML-format?
The XmlWriter has a Settings property of type XmlWriterSettings.
Try using that to specify the formatting to be used.
using (XmlWriter writer = nav.AppendChild())
{
writer.Settings.Indent = true;
XmlSerializer ser = new XmlSerializer(typeof(List<T>), overrides);
ser.Serialize(writer, _depositorsOperationsList);
}
More information here:
https://msdn.microsoft.com/en-us/library/kbef2xz3(VS.80).aspx
If you open your file with special XML editor or just with web navigator like chrome or internet explorer it will look like you want.
Just pasting snippet from my code that works fine..
Public Function ToXmlString() As String
Dim builder = New StringBuilder()
Dim xmlSerializer = New XmlSerializer(GetType(List(Of T)), New XmlRootAttribute(_rootNodeName))
Using writer = XmlWriter.Create(builder, New XmlWriterSettings() With {.OmitXmlDeclaration = True})
xmlSerializer.Serialize(writer, _source)
End Using
Return builder.ToString()
End Function
---- Converted C# Code ----
public string ToXmlString()
{
var builder = new StringBuilder();
var xmlSerializer = new XmlSerializer(typeof(List<T>), new XmlRootAttribute(_rootNodeName));
using (writer = XmlWriter.Create(builder, new XmlWriterSettings { OmitXmlDeclaration = true })) {
xmlSerializer.Serialize(writer, _source);
}
return builder.ToString();
}
where _rootNodeName is name of your root node of xml and _source is sourceList
And then to create XMLDoc from string --
XmlDocument xml = new XmlDocument();
xml.LoadXml(xmlString);

remove type declaration from XML while serialize

I need to get clear xml without any namespace and type declarations. Here is the serialized xml:
<placeBetRequest p1:type="PlaceBetRequestTeamed" xmlns:p1="http://www.w3.org/2001/XMLSchema-instance">
...
</placeBetRequest>
Need to someway configure XmlSerializer to set it don't add any attributes but only mine (if I set them with [XmlAttribte]
the clean xml need to looks like this:
<placeBetRequest>
...
</placeBetRequest>
Here is my serialization method:
public static string XmlConvert<T>(T obj, params Type[] wellKnownTypes) where T : class
{
var emptyNamepsaces = new XmlSerializerNamespaces(new[] { XmlQualifiedName.Empty });
var settings = new XmlWriterSettings {Indent = true, OmitXmlDeclaration = true};
XmlSerializer serializer = wellKnownTypes == null
? new XmlSerializer(typeof(T))
: new XmlSerializer(typeof(T), wellKnownTypes);
using (var stream = new StringWriter())
using (var writer = XmlWriter.Create(stream, settings))
{
serializer.Serialize(writer, obj, emptyNamepsaces);
return stream.ToString();
}
}
Please help. Thanks.
I've been using regex
string input =
"<p1:placeBetRequest p1:type=\"PlaceBetRequestTeamed\" xmlns:p1=\"http://www.w3.org/2001/XMLSchema-instance\">" +
"</p1:placeBetRequest>";
string pattern = #"(?'prefix'\</?)(?'ns'[^:]*:)";
string output = Regex.Replace(input, pattern, "${prefix}");

Retrieving Data From XML File

I seem to be having a problem with retrieving XML values with C#, which I know it is due to my very limited knowledge of C# and .XML.
I was given the following XML file
<PowerBuilderRunTimes>
<PowerBuilderRunTime>
<Version>12</Version>
<Files>
<File>EasySoap110.dll</File>
<File>exPat110.dll</File>
<File>pbacc110.dll</File>
</File>
</PowerBuilderRunTime>
</PowerBuilderRunTimes>
I am to process the XML file and make sure that each of the files in the exist in the folder (that's the easy part). It's the processing of the XML file that I have having a hard time with. Here is what I have done thus far:
var runtimeXml = File.ReadAllText(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
var doc = XDocument.Parse(runtimeXml);
var topElement = doc.Element("PowerBuilderRunTimes");
var elements = topElement.Elements("PowerBuilderRunTime");
foreach (XElement section in elements)
{
//pbVersion is grabbed earlier. It is the version of PowerBuilder
if( section.Element("Version").Value.Equals(string.Format("{0}", pbVersion ) ) )
{
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
}
}
My issue is that the String List is only ever populated with one value, "EasySoap110.dll", and everything else is ignored. Can someone please help me, as I am at a loss.
Look at this bit:
var files = section.Elements("Files");
var fileList = new List<string>();
foreach (XElement area in files)
{
fileList.Add(area.Element("File").Value);
}
You're iterating over each Files element, and then finding the first File element within it. There's only one Files element - you need to be iterating over the File elements within that.
However, there are definitely better ways of doing this. For example:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var fileList = (from runtime in doc.Root.Elements("PowerBuilderRunTime")
where (int) runtime.Element("Version") == pbVersion
from file in runtime.Element("Files").Elements("File")
select file.Value)
.ToList();
Note that if there are multiple matching PowerBuilderRunTime elements, that will create a list with all the files of all those elements. That may not be what you want. For example, you might want:
var doc = XDocument.Load(Path.Combine(configPath, Resource.PBRuntimes));
var runtime = doc.Root
.Elements("PowerBuilderRunTime")
.Where(r => (int) r.Element("Version") == pbVersion)
.Single();
var fileList = runtime.Element("Files")
.Elements("File")
.Select(x => x.Value)
.ToList();
That will validate that there's exactly one matching runtime.
The problem is, there's only one element in your XML, with multiple children. You foreach loop only executes once, for the single element, not for its children.
Do something like this:
var fileSet = files.Elements("File");
foreach (var file in fileSet) {
fileList.Add(file.Value);
}
which loops over all children elements.
I always preferred using readers for reading homegrown XML config files. If you're only doing this once it's probably over kill, but readers are faster and cheaper.
public static class PowerBuilderConfigParser
{
public static IList<PowerBuilderConfig> ReadConfigFile(String path)
{
IList<PowerBuilderConfig> configs = new List<PowerBuilderConfig>();
using (FileStream stream = new FileStream(path, FileMode.Open))
{
XmlReader reader = XmlReader.Create(stream);
reader.ReadToDescendant("PowerBuilderRunTime");
do
{
PowerBuilderConfig config = new PowerBuilderConfig();
ReadVersionNumber(config, reader);
ReadFiles(config, reader);
configs.Add(config);
reader.ReadToNextSibling("PowerBuilderRunTime");
} while (reader.ReadToNextSibling("PowerBuilderRunTime"));
}
return configs;
}
private static void ReadVersionNumber(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToDescendant("Version");
string version = reader.ReadString();
Int32 versionNumber;
if (Int32.TryParse(version, out versionNumber))
{
config.Version = versionNumber;
}
}
private static void ReadFiles(PowerBuilderConfig config, XmlReader reader)
{
reader.ReadToNextSibling("Files");
reader.ReadToDescendant("File");
do
{
string file = reader.ReadString();
if (!string.IsNullOrEmpty(file))
{
config.AddConfigFile(file);
}
} while (reader.ReadToNextSibling("File"));
}
}
public class PowerBuilderConfig
{
private Int32 _version;
private readonly IList<String> _files;
public PowerBuilderConfig()
{
_files = new List<string>();
}
public Int32 Version
{
get { return _version; }
set { _version = value; }
}
public ReadOnlyCollection<String> Files
{
get { return new ReadOnlyCollection<String>(_files); }
}
public void AddConfigFile(String fileName)
{
_files.Add(fileName);
}
}
Another way is to use a XmlSerializer.
[Serializable]
[XmlRoot]
public class PowerBuilderRunTime
{
[XmlElement]
public string Version {get;set;}
[XmlArrayItem("File")]
public string[] Files {get;set;}
public static PowerBuilderRunTime[] Load(string fileName)
{
PowerBuilderRunTime[] runtimes;
using (var fs = new FileStream(fileName, FileMode.Open, FileAccess.Read))
{
var reader = new XmlTextReader(fs);
runtimes = (PowerBuilderRunTime[])new XmlSerializer(typeof(PowerBuilderRunTime[])).Deserialize(reader);
}
return runtimes;
}
}
You can get all the runtimes strongly typed, and use each PowerBuilderRunTime's Files property to loop through all the string file names.
var runtimes = PowerBuilderRunTime.Load(string.Format("{0}\\{1}", configPath, Resource.PBRuntimes));
You should try replacing this stuff with a simple XPath query.
string configPath;
System.Xml.XPath.XPathDocument xpd = new System.Xml.XPath.XPathDocument(cofigPath);
System.Xml.XPath.XPathNavigator xpn = xpd.CreateNavigator();
System.Xml.XPath.XPathExpression exp = xpn.Compile(#"/PowerBuilderRunTimes/PwerBuilderRunTime/Files//File");
System.Xml.XPath.XPathNodeIterator iterator = xpn.Select(exp);
while (iterator.MoveNext())
{
System.Xml.XPath.XPathNavigator nav2 = iterator.Current.Clone();
//access value with nav2.value
}

Categories