Writing XML with C#

点点圈 提交于 2020-01-23 11:42:58

问题


My C# is a bit rusty and I've never written XML with it before. I'm having trouble getting the XML to write to a file if I attempt to write anything other than elements. Here is the test code that I have:

var guiPath = txtGuiPath.Text;
MessageBox.Show("Dumping File: " + guiPath);

try
{
    var writer = new XmlTextWriter("client_settings.xml", null);
    writer.WriteStartDocument();
    writer.WriteComment("Config generated on 01/01/01");
    writer.WriteStartElement("Config");
    writer.WriteStartElement("GuiPath");
    writer.WriteString(guiPath);
    writer.WriteEndElement();
    writer.WriteEndElement();
    writer.WriteEndDocument();
    writer.Close();
} catch (Exception ex) {
    MessageBox.Show(ex.Message);
}
MessageBox.Show("Finished Dumping");

If guiPath is blank I get the following XML:

<?xml version="1.0"?>
<!--Config generated on 01/01/01-->
<Config>
    <GuiPath />
</Config>

but if there is any text inside guiPath then nothing gets written to the file. I can even delete the client_settings.xml file and fire this code off over and over and the XML file never gets generated unless guiPath is empty. Passing something like "This is a test" to WriteString() works as well.

Update

Since I'm trying to write out a system path, that seems to be the problem. If I strip out all the backslashes it will write the resulting string correctly, but if I pass it to WriteString or WriteCData the XML will not write at all.

Update 2

Turns out that the reason I was having so many problems is because the XML file was being generated in whatever path guiPath was set to, not into the directory that the app was running from (so to me it looked like it wasn't being generated at all). So, if I had guiPath set to 'C:\Program Files\externalApp\appName.exe', it was saving the XML file as 'C:\ProgramFiles\externalApp\client_settings.xml' instead of in the startup folder for the app. Why, I don't know. I started passing Application.StartupPath and appended the filename to that and it works great now.

Thanks for all the help!


回答1:


You might want to examine the API in System.Xml.Linq. It's a bit of a more flexible approach to generating and writing XML. Writing your document might go roughly like this:

XDocument document = new XDocument();
document.Add(new XComment("Config generated on 01/01/01"));
document.Add(new XElement("Config", new XElement("GuiPath", guiPath)));

// var xmlWriter = new XmlTextWriter("client_settings.xml", null);
// document.WriteTo(xmlWriter);

// thanks to Barry Kelly for pointing out XDocument.Save()
document.Save("client_settings.xml");



回答2:


Why not create a simple class to hold all the data you need and then serialize it using XmlSerializer, rather than manually generating it line by line? You can even use the attributes in System.Xml.Serialization to control the output if you need:

using System;
using System.IO;
using System.Windows.Forms;
using System.Xml.Serialization;

namespace Foo
{    
    [XmlRoot(ElementName = "Config")]
    public class Config
    {        
        public String GuiPath { get; set; }

        public Boolean Save(String path)
        {
            using (var fileStream = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite))
            {
                try
                {
                    var serializer = new XmlSerializer(typeof(Config));
                    serializer.Serialize(fileStream, this);
                    return true;
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    // Other exception handling here
                    return false;
                }
            }
        }

        public static Config Load(String path)
        {
            using (var fileStream = File.Open(path, FileMode.Open, FileAccess.Read))
            {
                try
                {
                    var serializer = new XmlSerializer(typeof(Config));
                    return (Config)serializer.Deserialize(fileStream);
                }
                catch(Exception ex)
                {
                    MessageBox.Show(ex.Message);
                    // Other exception handling here
                    return null;
                }
            }
        }
    }
}

This way you don't have to worry about manually encoding strings if they have odd characters - the serializer will do that for you.

This also has the added benefit of being able to be serialized back into the class so you can have strongly typed access to the structure, if you ever need to do that.




回答3:


Hmm, seems likely that the "real" guiPath contains characters that are breaking XML validation and the XmlTextWriter with it.

May I suggest you try .WriteCData() (instead of .WriteString() that is)




回答4:


What do you want the output to be? If you were looking for something like:

<?xml version="1.0"?>
<!--Config generated on 01/01/01-->
<Config>
    GuiPath="c:\some\path\here\"
</Config>

Then you need to change your WriteString to:

writer.WriteAttributeString("GuiPath", guiPath);

Or, if you wanted:

<GuiPath>c:\some\path\here\</GuiPath>

Then you need to write

writer.WriteElementString("GuiPath", guiPath);



回答5:


Nobody else has mentioned it, but I think I had better: strongly consider using the using statement when working with IDisposable implementations such as XmlTextWriter etc.

This is important not just for closing resources, such as the underlying stream or text writer, but also to make sure any buffers have been flushed, and to make sure any remaining unclosed elements are closed.

So, when you see mquander's anwser, consider this instead:

using (var xmlWriter = new XmlTextWriter("client_settings.xml", null))
{
    // ...
}

Similarly, in Daniel's answer, don't blindly swallow exceptions, and strongly consider using the using statement on the return value of File.Open (which probably ought to be File.OpenText to be idiomatic, but there are many other shortcomings in style with Daniel's answer at the time of writing).




回答6:


I would use the System.XML.Linq.XElement class

Note sure about the comment but the Config part would go something like this.

XElement root = new XElement("Config");
root.Add(new XElement("GuiPath", guiPath);
root.Save("client_settings.xml");

Edit: mquander's example is better. Look at that.




回答7:


You need to escapify the contents before writing them out, to make sure that they're valid strings. I don't know of a .NET routine to do it automatically, unfortunately -- the question has been asked here before.



来源:https://stackoverflow.com/questions/489173/writing-xml-with-c-sharp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!