Написание XML с C#
Мой C# немного ржавый, и я никогда раньше не писал с ним XML. У меня проблемы с получением XML для записи в файл, если я пытаюсь записать что-либо, кроме элементов. Вот тестовый код, который у меня есть:
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");
Если guiPath пуст, я получаю следующий XML:
<?xml version="1.0"?>
<!--Config generated on 01/01/01-->
<Config>
<GuiPath />
</Config>
но если внутри guiPath есть какой-либо текст, то в файл ничего не записывается. Я даже могу удалить файл client_settings.xml и запускать этот код снова и снова, и XML-файл никогда не генерируется, если guiPath не пуст. Передача чего-то вроде "Это тест" в WriteString() также работает.
Обновить
Поскольку я пытаюсь записать системный путь, похоже, это проблема. Если я удаляю все обратные косые черты, он будет правильно записывать полученную строку, но если я передам ее в WriteString или WriteCData, XML вообще не будет записываться.
Обновление 2
Оказывается, причина, по которой у меня было так много проблем, заключается в том, что файл XML генерировался по какому-либо заданному пути guiPath, а не в каталоге, из которого запускалось приложение (поэтому для меня это выглядело так, как будто оно не генерировалось). совсем). Таким образом, если для параметра guiPath было указано "C:\Program Files\externalApp\appName.exe", он сохранял XML-файл как "C:\ProgramFiles\externalApp\client_settings.xml", а не в папке запуска приложения., Почему я не знаю. Я начал передавать Application.StartupPath и добавил к нему имя файла, и теперь оно отлично работает.
Спасибо за помощь!
7 ответов
Возможно, вы захотите изучить API в System.Xml.Linq. Это немного более гибкий подход к созданию и написанию XML. Написание вашего документа может выглядеть примерно так:
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");
Почему бы не создать простой класс для хранения всех необходимых вам данных, а затем сериализовать их с помощью XmlSerializer, а не создавать их вручную построчно? Вы даже можете использовать атрибуты в 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;
}
}
}
}
}
Таким образом, вам не нужно беспокоиться о ручном кодировании строк, если они содержат нечетные символы - сериализатор сделает это за вас.
Это также имеет дополнительное преимущество, заключающееся в возможности сериализации обратно в класс, так что вы можете иметь строго типизированный доступ к структуре, если вам когда-нибудь понадобится это сделать.
Хм, кажется вероятным, что "настоящий" guiPath
содержит символы, которые нарушают проверку XML и XmlTextWriter с ним.
Могу ли я предложить вам попробовать .WriteCData()
(вместо .WriteString()
то есть)
Никто другой не упомянул об этом, но я думаю, что мне лучше: решительно рассмотреть возможность использования using
заявление при работе с IDisposable
реализации, такие как XmlTextWriter
и т.п.
Это важно не только для закрытия ресурсов, таких как базовый поток или средство записи текста, но также для того, чтобы убедиться, что все буферы сброшены, и чтобы убедиться, что все оставшиеся незакрытые элементы закрыты.
Итак, когда вы видите ответ mquander, рассмотрите это вместо этого:
using (var xmlWriter = new XmlTextWriter("client_settings.xml", null))
{
// ...
}
Точно так же, в ответе Даниэля, не слепо глотайте исключения, и настоятельно рекомендуется использовать using
заявление о возвращаемом значении File.Open
(что, вероятно, должно быть File.OpenText
быть идиоматичным, но есть много других недостатков в стиле с ответом Даниэля во время написания).
Что вы хотите, чтобы вывод был? Если вы искали что-то вроде:
<?xml version="1.0"?>
<!--Config generated on 01/01/01-->
<Config>
GuiPath="c:\some\path\here\"
</Config>
Затем вам нужно изменить свой WriteString на:
writer.WriteAttributeString("GuiPath", guiPath);
Или, если вы хотели:
<GuiPath>c:\some\path\here\</GuiPath>
Тогда вам нужно написать
writer.WriteElementString("GuiPath", guiPath);
Я бы использовал класс System.XML.Linq.XElement
Обратите внимание на комментарий, но часть Config будет выглядеть примерно так.
XElement root = new XElement("Config");
root.Add(new XElement("GuiPath", guiPath);
root.Save("client_settings.xml");
Изменить: пример Mquander лучше. Посмотри на это.
Вы должны экранировать содержимое перед тем, как записывать их, чтобы убедиться, что они являются допустимыми строками. К сожалению, я не знаю.NET-процедуры, которая бы делала это автоматически - вопрос уже задавался здесь раньше.