简体   繁体   English

用C#编写XML

[英]Writing XML with C#

My C# is a bit rusty and I've never written XML with it before. 我的C#有点生疏,我以前从未用它编写XML。 I'm having trouble getting the XML to write to a file if I attempt to write anything other than elements. 如果我尝试编写除元素之外的任何内容,我就无法将XML写入文件。 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: 如果guiPath为空,我会得到以下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. 但如果guiPath中有任何文本,那么没有任何内容写入文件。 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. 我甚至可以删除client_settings.xml文件并反复激活此代码,除非guiPath为空,否则永远不会生成XML文件。 Passing something like "This is a test" to WriteString() works as well. 将类似“This is a test”的内容传递给WriteString()也可以。

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. 如果我删除所有反斜杠,它将正确地写入结果字符串,但如果我将它传递给WriteString或WriteCData,则XML根本不会写入。

Update 2 更新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). 事实证明我遇到这么多问题的原因是因为XML文件是在guiPath设置的任何路径中生成的,而不是在应用程序运行的目录中生成的(所以对我来说看起来它没有生成在所有)。 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. 因此,如果我将guiPath设置为'C:\\ Program Files \\ externalApp \\ appName.exe',它将XML文件保存为'C:\\ ProgramFiles \\ externalApp \\ client_settings.xml',而不是在应用的启动文件夹中。 Why, I don't know. 为什么,我不知道。 I started passing Application.StartupPath and appended the filename to that and it works great now. 我开始传递Application.StartupPath并将文件名附加到它,现在它工作得很好。

Thanks for all the help! 感谢您的帮助!

You might want to examine the API in System.Xml.Linq. 您可能想要检查System.Xml.Linq中的API。 It's a bit of a more flexible approach to generating and writing XML. 这是一种更灵活的生成和编写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");

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? 为什么不创建一个简单的类来保存所需的所有数据,然后使用XmlSerializer对其进行序列化,而不是逐行手动生成它? You can even use the attributes in System.Xml.Serialization to control the output if you need: 如果需要,您甚至可以使用System.Xml.Serialization中的属性来控制输出:

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. 这还有一个额外的好处,就是能够序列化回到类中,这样您就可以对结构进行强类型访问,如果您需要这样做的话。

Hmm, seems likely that the "real" guiPath contains characters that are breaking XML validation and the XmlTextWriter with it. 嗯,似乎“真正的” guiPath包含破坏XML验证的字符和带有它的XmlTextWriter。

May I suggest you try .WriteCData() (instead of .WriteString() that is) 我建议你尝试.WriteCData() (而不是.WriteString()

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: 然后你需要将WriteString更改为:

writer.WriteAttributeString("GuiPath", guiPath);

Or, if you wanted: 或者,如果你想:

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

Then you need to write 然后你需要写

writer.WriteElementString("GuiPath", guiPath);

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. 没有其他人提到它,但我认为我做得更好:在using IDisposable实现(如XmlTextWriter等)时强烈考虑使用using语句。

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: 所以,当你看到mquander的anwser时 ,请考虑一下:

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). 同样,在Daniel的回答中 ,不要盲目地吞下异常,并强烈考虑在File.Open的返回值上使用using语句(可能应该是File.OpenText是惯用的,但是风格还有许多其他缺点丹尼尔在撰写本文时的答案)。

I would use the System.XML.Linq.XElement class 我会使用System.XML.Linq.XElement类

Note sure about the comment but the Config part would go something like this. 请注意注释,但Config部分会是这样的。

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

Edit: mquander's example is better. 编辑: mquander的例子更好。 Look at that. 看那个。

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. 不幸的是,我不知道自动执行它的.NET例程 - 之前已经问过这个问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM