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
Related
I am trying to add a custom XML part to Word. What I am doing is I have generated an XML model that binds all my data in the Word file. This is how I am trying to fill it, but nothing is being added:
public void FeedCustomXmlParts(MyModel model, string xmlns)
{
var ser = new XmlSerializer(typeof(MyModel), xmlns);
using (var ms = new MemoryStream())
{
ser.Serialize(ms, model);
CustomXmlPart partLabel = GetCustomXmlPart(_wordDoc.MainDocumentPart, xmlns);
ms.Position = 0;
partLabel.FeedData(ms);
ms.Flush();
}
}
private static CustomXmlPart GetCustomXmlPart(MainDocumentPart mainDocumentPart, string targetNs)
{
if (mainDocumentPart == null)
{
throw new ArgumentNullException(nameof(mainDocumentPart));
}
return (from part in mainDocumentPart.CustomXmlParts
let xElement = part.GetXDocument().Root
where xElement != null
let ns = xElement.GetDefaultNamespace()
where ns == targetNs
select part).FirstOrDefault();
}
What am I doing wrong?
According to your description, I understand that you want to add custom XML parts to the word.
The following code example adds a custom XML part to a specified document.
private void AddCustomXmlPartToActiveDocument(Word.Document document)
{
string xmlString =
"<?xml version=\"1.0\" encoding=\"utf-8\" ?>" +
"<employees xmlns=\"http://schemas.microsoft.com/vsto/samples\">" +
"<employee>" +
"<name>Karina Leal</name>" +
"<hireDate>1999-04-01</hireDate>" +
"<title>Manager</title>" +
"</employee>" +
"</employees>";
Office.CustomXMLPart employeeXMLPart = document.CustomXMLParts.Add(xmlString, missing);
}
For more information, please refer to:To add a custom XML part to a Word document
Hope that helps!
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 am currently working with an xml file which usually should contain a list of custom objects (List), but from time to time can simply contain a string node with a message. I have a code which deserializes this file:
private T DeserializeFile<T>(string filePath)
{
StreamReader readFileStream = new StreamReader(#filePath);
var serializerObj = new XmlSerializer(typeof(T));
return (T)serializerObj.Deserialize(readFileStream);
}
This works for List but for simple string throws an error (InvalidOperationException - Root element is missing, or " was not expected."). How can I detect the case when the file contains only the string element and return null from the function?
Basically this is what I want to do:
private T DeserializeFile<T>(string filePath)
{
StreamReader readFileStream = new StreamReader(#filePath);
var serializerObj = new XmlSerializer(typeof(T));
try
{
return (T)serializerObj.Deserialize(readFileStream);
}
catch (Exception ex)
{
return null;
}
}
This should do what you're trying to do. It uses XDocument to load and parse the file, so that it can check if there are any elements before trying to deserialize it.
private T DeserializeFile<T>(string filePath)
{
var xdoc = XDocument.Load(filePath);
if (xdoc.Root.Elements().Any())
{
var serializerObj = new XmlSerializer(typeof(T));
return (T)serializerObj.Deserialize(xdoc.CreateReader());
}
else
return default(T);
}
This assumes that you always have valid XML files, with the only difference being whether the root contains more elements or just text, e.g. a list like
<someRoot>
<someObj>
</someObj>
<someObj>
</someObj>
</someRoot>
And a "string" like
<someRoot>just a string</someRoot>
I am reading a .docx file using OpenXML in C#. It reads everything correctly but strangely, the content of textbox is being read thrice. What could be wrong? Here is the code to read .docx:
public static string TextFromWord(String file)
{
const string wordmlNamespace = "http://schemas.openxmlformats.org/wordprocessingml/2006/main";
StringBuilder textBuilder = new StringBuilder();
using (WordprocessingDocument wdDoc = WordprocessingDocument.Open(file, false))
{
// Manage namespaces to perform XPath queries.
NameTable nt = new NameTable();
XmlNamespaceManager nsManager = new XmlNamespaceManager(nt);
nsManager.AddNamespace("w", wordmlNamespace);
// Get the document part from the package.
// Load the XML in the document part into an XmlDocument instance.
XmlDocument xdoc = new XmlDocument(nt);
xdoc.Load(wdDoc.MainDocumentPart.GetStream());
XmlNodeList paragraphNodes = xdoc.SelectNodes("//w:p", nsManager);
foreach (XmlNode paragraphNode in paragraphNodes)
{
XmlNodeList textNodes = paragraphNode.SelectNodes(".//w:t", nsManager);
foreach (System.Xml.XmlNode textNode in textNodes)
{
textBuilder.Append(textNode.InnerText);
}
textBuilder.Append(Environment.NewLine);
}
}
return textBuilder.ToString();
}
The part of file I am talking about is:
The result is: I read it in a test application like this:
What's wrong here?
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);
}