I have a problem with some XML files.
I have about 500+ text files which i read from and extract data and export it to an XML. However, the XML generated is truncated halfway.
<Maps>
<Location>
</Location>
<Locat
It truncates somewhat like that. When i try to open it in notepad, it seems to use up the whole page of notepad. Could it be due to a memory issue since i have quite a large amount of data. Is there any work around on it. I am running the program on a VM
private XmlTextWriter writer = new XmlTextWriter("Maps.xml", System.Text.Encoding.UTF8);
static void Main(string[] args)
{
Program prog = new Program();
String[] filenames = Directory.GetFiles(#"maps_new");
prog.writer.WriteStartDocument();
prog.writer.WriteStartElement("Maps");
foreach (String file in filenames)
{
prog.extractToXML(file);
}
prog.writer.WriteEndElement();
prog.writer.WriteEndDocument();
}
public void extractToXML(String filename)
{
XPathNodeIterator NodeIter;
XPathDocument xmldoc = new XPathDocument(filename);
XPathNavigator nav = xmldoc.CreateNavigator();
String query = "//Schema/#tree";
NodeIter = nav.Select(query);
writer.WriteStartElement("file");
writer.WriteStartAttribute("name");
writer.WriteString(extractFileName(filename));
writer.WriteEndAttribute();
while (NodeIter.MoveNext())
{
writer.WriteStartElement("type");
writer.WriteString(extractFileName(NodeIter.Current.Value.ToString()));
writer.WriteEndElement();
}
NodeIter = nav.Select("//Location");
while (NodeIter.MoveNext())
{
writer.WriteStartElement("Location");
writer.WriteString(NodeIter.Current.Value.ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
}
When you use a writer, be sure to Close it when you are done:
try addind this:
//rest of code omitted
foreach (String file in filenames)
{
prog.extractToXML(file);
}
prog.writer.WriteEndElement();
prog.writer.WriteEndDocument();
prog.writer.Flush(); //<-- here
prog.writer.Close(); //<-- and here
and better yet: use a using statement and make extractToXML static:
static void Main(string[] args)
{
using(var writer = new XmlTextWriter("Maps.xml", System.Text.Encoding.UTF8))
{
String[] filenames = Directory.GetFiles(#"maps_new");
writer.WriteStartDocument();
writer.WriteStartElement("Maps");
foreach (String file in filenames)
{
extractToXML(file, writer);
}
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Flush();
writer.Close();
}
}
public static void extractToXML(String filename, XmlTextWriter wirter)
{
XPathNodeIterator NodeIter;
XPathDocument xmldoc = new XPathDocument(filename);
XPathNavigator nav = xmldoc.CreateNavigator();
String query = "//Schema/#tree";
NodeIter = nav.Select(query);
writer.WriteStartElement("file");
writer.WriteStartAttribute("name");
writer.WriteString(extractFileName(filename));
writer.WriteEndAttribute();
while (NodeIter.MoveNext())
{
writer.WriteStartElement("type");
writer.WriteString(extractFileName(NodeIter.Current.Value.ToString()));
writer.WriteEndElement();
}
NodeIter = nav.Select("//Location");
while (NodeIter.MoveNext())
{
writer.WriteStartElement("Location");
writer.WriteString(NodeIter.Current.Value.ToString());
writer.WriteEndElement();
}
writer.WriteEndElement();
}
Related
I have a problem with saving data from XML URL nodes, using XMLReader, to a text file. Can you please help me out? I don't know how to do it.
Here is the code:
namespace XMLdemo2
{
class Program
{
static void Main(string[] args)
{
// Start with XmlReader object
String URLString = "https://www.shortcut.lv/xmls/tiesraide/ltv1.xml";
XmlTextReader reader = new XmlTextReader(URLString);
{
while (reader.Read())
{
if (reader.IsStartElement())
{
switch (reader.Name.ToString())
{
case "auth_token":
Console.WriteLine("Tokens IR : " + reader.ReadString());
break;
}
//Console.WriteLine("");
}
}
Console.ReadKey();
}
}
}
}
You can try something easier like this (if it's only one line you want to read)
XmlDocument xmlDoc = new XmlDocument();
xmlDoc.Load("https://www.shortcut.lv/xmls/tiesraide/ltv1.xml");
XmlNode authTokenNode = xmlDoc.SelectSingleNode("//auth_token");
if(authTokenNode != null)
Console.WriteLine(authTokenNode.InnerText);
If it is multiple lines
XmlDocument xmlDoc = new XmlDocument();
XmlNodeList itemNodes = xmlDoc.SelectNodes("//auth_token");
foreach(XmlNode itemNode in itemNodes)
{
if((itemNode != null)
Console.WriteLine(itemNode.InnerText);
}
I am trying append a serialized object to an existing xml file beneath the root element, which I thought would be simple but is proving to be a little challenging.
The problem is in the AddShortcut method but I added some more code for completeness.
I believe what I need to do is:
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument.
The trouble is in steps 2 and 3. I have tried different variations but I think using XPathNavigator somehow to find the "root" node to append under is a step in the right direction.
I have also looked at almost every question on Stack Overflow on the subject.
Any suggestions welcome. Here is my code
class XmlEngine
{
public string FullPath { get; set; } // the full path to the xmlDocument
private readonly XmlDocument xDoc;
public XmlEngine(string fullPath, string startElement, string[] rElements)
{
FullPath = fullPath;
xDoc = new XmlDocument();
CreateXmlFile(FullPath, startElement, rElements);
}
public void CreateXmlFile(string path, string startElement, string[] rElements)
{
try
{
if (!File.Exists(path))
{
// create a txt writer
XmlTextWriter wtr = new XmlTextWriter(path, System.Text.Encoding.UTF8);
// make sure the file is well formatted
wtr.Formatting = Formatting.Indented;
wtr.WriteProcessingInstruction("xml", "version='1.0' encoding='UTF-8'");
wtr.WriteStartElement(startElement);
wtr.Close();
// write the top level root elements
writeRootElements(path, rElements);
}
}
catch (Exception ex)
{
Console.WriteLine("Error: " + ex.Message);
Console.WriteLine("Could not create file: " + path);
}
}
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
rootNode = xDoc.AppendChild(xDoc.CreateElement("Shortcuts"));
var serializer = new XmlSerializer(s.GetType());
using (var writer = new StreamWriter(FullPath, true))
{
XmlWriterSettings ws = new XmlWriterSettings();
ws.OmitXmlDeclaration = true;
serializer.Serialize(writer, s);
}
xDoc.Save(FullPath);
}
}
This code sample worked for me:
xml:
<?xml version="1.0" encoding="UTF-8"?>
<Launchpad>
<Shortcuts>
<Shortcut Id="1">
<Type>Folder</Type>
<FullPath>C:\SomePath</FullPath>
<Name>SomeFolderName</Name>
</Shortcut>
</Shortcuts>
</Launchpad>
Method:
public void AddShortcut(Shortcut s)
{
xDoc.Load(FullPath);
var rootNode = xDoc.GetElementsByTagName("Shortcuts")[0];
var nav = rootNode.CreateNavigator();
var emptyNamepsaces = new XmlSerializerNamespaces(new[] {
XmlQualifiedName.Empty
});
using (var writer = nav.AppendChild())
{
var serializer = new XmlSerializer(s.GetType());
writer.WriteWhitespace("");
serializer.Serialize(writer, s, emptyNamepsaces);
writer.Close();
}
xDoc.Save(FullPath);
}
load the file into an XmlDocument.
navigate to the node I want to append beneath (here the node name is Shortcuts).
create some type of writer and then serialize the object.
save the XmlDocument
So:
public void AddShortcut(Shortcut s)
{
// 1. load existing xml
xDoc.Load(FullPath);
// 2. create an XML node from object
XmlElement node = SerializeToXmlElement(s);
// 3. append that node to Shortcuts node under XML root
var shortcutsNode = xDoc.CreateElement("Shortcuts")
shortcutsNode.AppendChild(node);
xDoc.DocumentElement.AppendChild(shortcutsNode);
// 4. save changes
xDoc.Save(FullPath);
}
public static XmlElement SerializeToXmlElement(object o)
{
XmlDocument doc = new XmlDocument();
using(XmlWriter writer = doc.CreateNavigator().AppendChild())
{
new XmlSerializer(o.GetType()).Serialize(writer, o);
}
return doc.DocumentElement;
}
This post
this is my cod i want with this get all file in directory and end write all in xml file
private void button3_Click(object sender, EventArgs e)
{
XmlDocument doc = new XmlDocument();
string appPath = Path.GetDirectoryName(Application.ExecutablePath);
string folder = appPath;//Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName) + #"\Archive\";
string filter = "*.*";
string[] files = Directory.GetFiles(folder, filter);
foreach (string item in files)
{
string string1 = item;
string string2 = appPath;
string result = string1.Replace(string2, "");
MessageBox.Show(result);
doc.LoadXml("<item><name>#" + result + " </name></item>");
// Save the document to a file and auto-indent the output.
using (XmlTextWriter writer = new XmlTextWriter("data.xml", null))
{
writer.Formatting = Formatting.Indented;
doc.Save(writer);
writer.Close();
}
}
}
with this code i get my file in directory and remove path
for example C://folder1/folder2/bin/app.exe
to app.exe
its okay but in the end in xml just write one file
XML Result
<?xml version="1.0"?>
<item>
<name>#\WindowsFormsApplication8.vshost.exe.manifest </name>
</item>
Here:
doc.LoadXml("<item><name>#" + result + " </name></item>");
Every time your loop repeats, you're overwriting all of the XML in your XmlDocument.
If you want to use XmlDocument, try this instead, although there are other (Cleaner) ways to output XML.
var doc = new XmlDocument();
var root = doc.AppendChild(doc.CreateElement("Item"));
foreach (var item in files)
{
var name = root.AppendChild(doc.CreateElement("Name"));
name.InnerText = item;
}
var xmlWriterSettings = new XmlWriterSettings { Indent = true };
using (var writer = XmlWriter.Create("data.xml", xmlWriterSettings))
{
doc.Save(writer);
}
Using XmlSerialiser (cleaner C# code than XDocument):
public class Program
{
[XmlType("Item")]
public class Item
{
[XmlElement("Name")]
public string[] Files { get; set; }
}
static string SerialiseToXml<T>(T obj, bool isFormatted = false)
{
var ns = new XmlSerializerNamespaces();
ns.Add("", "");
var stringBuilder = new StringBuilder();
var xmlWriterSettings = new XmlWriterSettings { OmitXmlDeclaration = true, Indent = isFormatted };
using (var xmlWriter = XmlWriter.Create(stringBuilder, xmlWriterSettings))
{
var serializer = new XmlSerializer(obj.GetType());
serializer.Serialize(xmlWriter, obj, ns);
return stringBuilder.ToString();
}
}
static void Main(string[] args)
{
string[] files = {"Apple.txt", "Orange.exe", "Pear.docx", "Banana.xml", "Papaya.xls", "Passionfruit.cs"};
var item = new Item {Files = files};
var xml = SerialiseToXml(item, true);
Console.WriteLine(xml);
}
}
You are overwriting your items.
Here's the code that will write a proper xml:
XmlDocument doc = new XmlDocument();
string appPath = Directory.GetCurrentDirectory();
string folder = appPath;
string filter = "*.*";
string[] files = Directory.GetFiles(folder, filter);
using (XmlTextWriter writer = new XmlTextWriter("data.xml", null))
{
writer.WriteStartDocument();
writer.WriteStartElement("Items");
foreach (string item in files)
{
string string1 = item;
string string2 = appPath;
string result = string1.Replace(string2, "");
writer.WriteElementString("Item","", result);
Console.WriteLine(result);
writer.Formatting = Formatting.Indented;
}
writer.WriteEndElement();
writer.WriteEndDocument();
writer.Close();
doc.Save(writer);
}
And here's the sample xml,
<?xml version="1.0"?>
<Items>
<Item>\ConsoleApplication1.exe</Item>
<Item>\ConsoleApplication1.exe.config</Item>
<Item>\ConsoleApplication1.pdb</Item>
<Item>\ConsoleApplication1.vshost.exe</Item>
<Item>\ConsoleApplication1.vshost.exe.config</Item>
<Item>\ConsoleApplication1.vshost.exe.manifest</Item>
<Item>\data.xml</Item>
</Items>
thanks for best answers.
in my directory too i have 3 folder and There are more files in any folder ,i want any files in folders write in my xml
For Example
<Items>
<Item>\ConsoleApplication1.exe</Item>
<Item>\ConsoleApplication1.exe.config</Item>
<Item>\ConsoleApplication1.pdb</Item>
<Item>\ConsoleApplication1.vshost.exe</Item>
<Item>\ConsoleApplication1.vshost.exe.config</Item>
<Item>..folder1\gold.dll</Item>
<Item>..images\exit.png</Item>
I have the following code for writing XML file based on datacontracts
public static void LogDataContractToFile(string XMLStringToLog, string filePathAndName)
{
//String documentPath = string.Empty;
String xmlObject = string.Empty;
FileInfo fileinfo;
XmlDocumentFragment xmlDocumentFragment;
XmlTextWriter xmlWriter;
XmlDocument xmlDocument = null;
lock (LogDataContractToFileLock)
{
filePathAndName = filePathAndName.ToLower();
while (_workingWithFile.Contains(filePathAndName))
Thread.Sleep(1000);
_workingWithFile.Add(filePathAndName.ToLower());
try
{
#region Create XMLFile
fileinfo = new FileInfo(filePathAndName);
if (!fileinfo.Exists)
{
DirectoryInfo info = new DirectoryInfo(fileinfo.DirectoryName);
if (info.Exists == false)
info.Create();
using (xmlWriter = new XmlTextWriter(filePathAndName, System.Text.Encoding.UTF8))
{
xmlWriter.Formatting = Formatting.Indented;
xmlWriter.WriteStartDocument();
xmlWriter.WriteStartElement("root");
xmlWriter.WriteStartElement("objects");
xmlWriter.WriteEndElement();
xmlWriter.WriteEndElement();
xmlWriter.WriteEndDocument();
xmlWriter.Close();
}
}
else
{
//Se så att filen är 50 MB eller mindre
while (fileinfo.Length > 52428800)
{
xmlDocument = new XmlDocument();
xmlDocument.Load(filePathAndName);
xmlDocument.RemoveChild(xmlDocument.LastChild);
xmlDocument.Save(filePathAndName);
xmlDocument = null;
}
}
#endregion
xmlObject = XMLStringToLog;
//Open document
xmlDocument = new XmlDocument();
xmlDocument.Load(filePathAndName);
//Create a new fragment in current document
xmlDocumentFragment = xmlDocument.CreateDocumentFragment();
xmlDocumentFragment.InnerXml = xmlObject;
//Add new fragment after the first child
xmlDocument.DocumentElement.InsertBefore(xmlDocumentFragment, xmlDocument.DocumentElement.FirstChild);
xmlDocument.Save(filePathAndName);
xmlDocument = null;
}
finally
{
_workingWithFile.Remove(filePathAndName.ToLower());
}
}
}
The problem is that from time to time I get a The process cannot access the file exception? XmlDocument do not have any dispose so I can´t use using. How should this be handled properly?
Note that Im stuck on .NET 4.0.
To safely check for an element and add it if it doesn't exist you should use a ConcurrentDictionary:
private readonly ConcurrentDictionary<string,bool> _workingWithFile = new ConcurrentDictionary<string,bool>();
public static void LogDataContractToFile(string XMLStringToLog, string filePathAndName)
{
...
lock (LogDataContractToFileLock)
{
...
while(!_workingWithFile.TryAdd(filePathAndName, true))
{
Thread.Sleep(1000);
}
...
try
{
...
}
finally
{
//Perhaps check the result here.
bool result;
_workingWithFile.TryRemove(filePathAndName, out result);
}
}
}
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();
}
}