Как сохранить и получить значения из Xml без приведения?
Что-то, что я действительно ненавижу, это приведение каждого элемента или значения атрибута из файла XML.
Этот момент я создаю в сотнях модулей методов, в которых указывается, как преобразовать объект в XmlFile. Поверьте, это очень устало. Так что я подумываю об альтернативе.
Я занимался расследованием XSD, я не уверен, будет ли это моим спасением. Я использую Linq для Xml, чтобы сохранить и получить значения. Я имею в виду, мои объекты составлены так:
- Foo1 : Foo
- Range1 : Range
- X : int
- Y : int
- ...
Как видите, у них много узлов. Есть ли другая альтернатива для этого? Я имею в виду, сильно печатает.
4 ответа
Вы можете попробовать эти методы расширения XElement: http://searisen.com/xmllib/extensions.wiki
Вот пример силы этого, учитывая этот XML из другого поста:
<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<PatchCreation
Id="224C316C-5894-4771-BABF-21A3AC1F75FF"
CleanWorkingFolder="yes"
OutputPath="patch.pcp"
WholeFilesOnly="yes">
<PatchInformation
Description="Update Patch"
Comments="Update Patch"
ShortNames="no"
Languages="1033"
Compressed="yes"
Manufacturer="me"/>
<PatchMetadata
AllowRemoval="yes"
Description="Update Patch"
ManufacturerName="me"
TargetProductName="Update"
MoreInfoURL="http://andrewherrick.com/"
Classification="Update"
DisplayName="Update Patch"/>
<Family DiskId="5000"
MediaSrcProp="Sample"
Name="Update"
SequenceStart="5000">
<UpgradeImage SourceFile="c:\new.msi" Id="PatchUpgrade">
<TargetImage SourceFile="c:\old.msi" Order="2" Id="PatchUpgrade" IgnoreMissingFiles="no" />
</UpgradeImage>
</Family>
<PatchSequence PatchFamily="SamplePatchFamily"
Sequence="1.0.0.0"
Supersede="yes" />
</PatchCreation>
</Wix>
Это устанавливает значение атрибута SourceFile тега UpgradeImage и тега TargetImage внутри UpgradeImage и его SourceFile.
XElement wix = XElement.Load(xmlFile1.FullName);
wix.Set("PatchCreation/Family/UpgradeImage/SourceFile", "upgrade path", true)
.Set("TargetImage/SourceFile", "target path", true);
Вы также можете получить их значения таким же образом (без приведения).
string upgradeSource = wix.Get("PatchCreation/Family/UpgradeImage/SourceFile", string.Empty);
string targetSource = wix.Get("PatchCreation/Family/UpgradeImage/TargetImage/SourceFile", string.Empty);
Или это можно записать как:
XElement upgradeImage = wix.GetElement("PatchCreation/Family/UpgradeImage");
string upgradeSource = upgradeImage.Get("SourceFile", string.Empty);
string targetSource = upgradeImage.Get("TargetImage/SourceFile", string.Empty);
Чтобы получить список целых чисел:
<root>
<path>
<list>
<value>1</value>
<value>12</value>
<value>13</value>
<value>14</value>
<value>15</value>
</list>
</path>
</root>
Используйте метод GetEnumerable():
List<int> list = root
.GetEnumerable("path/list/value", xvalue => xvalue.Get(null, int.MinValue));
.ToList();
Чтобы установить новый список целых:
var list2 = new int[] { 1, 3, 4, 5, 6, 7, 8, 9, 0 };
root.SetEnumerable("path/list", list2, a => new XElement("value", a));
Что приводит к этому новому XML:
<root>
<path>
<list>
<value>1</value>
<value>3</value>
<value>4</value>
<value>5</value>
<value>6</value>
<value>7</value>
<value>8</value>
<value>9</value>
<value>0</value>
</list>
</path>
</root>
Лучше всего использовать XmlSerialization. Так что сериализуйте в XML, а затем десериализуйте в объекты, тогда вам не нужно использовать приведение.
Иногда XML создается другим способом, отличным от сериализации. Тем не менее, вы все равно можете создавать классы, которые представляют вашу XML-структуру и десериализацию.
Например:
Public Class Foo
{
public Range Range {get; set;}
}
public class Range
{
public int X {get; set;}
public int Y {get; set;}
}
Тогда вы используете это:
XmlSerializer ser = new XmlSerializer(typeof(Foo));
Я часто использую XSD для проверки структуры XML, частично именно по этой причине, и поскольку у меня нет конструкторов по умолчанию и / или нет личных полей, XMLSerialization
тоже не часто вариант.
Если XML проверяется без ошибок, тогда я использую Linq2Xml и использую различные Parse(String s)
методы для получения данных того типа, который требуется моему классу, но я пока не нашел чистого решения, которое бы делало это без какого-либо преобразования.
Этапы проверки позволяют избежать исключений из-за неправильного типа данных.
var query = from tst in xml.Elements("test")
select new
{
int1 = Int32.Parse(tst.Element("int1").Value),
double1 = Double.Parse(tst.Element("double1").Value),
double2 = Double.Parse(tst.Element("double2").Value),
}
РЕДАКТИРОВАТЬ: Добавлена информация, чтобы ответить на комментарий "
Вы можете создать XSD непосредственно из графического интерфейса Visual Studio, и есть другие инструменты, которые делают это тоже, но я обычно просто использую Visual Studio. Откройте XML в редакторе, затем в меню XML выберите "Создать схему" (путь показан на снимке).
Полученный XSD является очень простым. Он попытался пройти и "угадать" соответствующий тип данных для каждого узла, и он не содержит каких-либо дополнительных ограничений, но он делает достойную работу по созданию инфраструктуры для вас.
Как только это будет сделано, вы можете пойти и настроить типы данных, чтобы они лучше соответствовали вашим потребностям (при желании), и вы можете добавить свои собственные ограничения к данным (например, требовать, чтобы xs:int
значение находится в диапазоне от 0 до 50 или xs:string
значение должно быть длиной менее 10 символов - существуют десятки других возможностей, но это должно дать вам представление).
На самом деле я просто играл с языком XSD, и чем больше я это делал, тем он становился все легче. Сайт W3Schools был неоценим.
Мы используем Linq To Xsd для создания строго оберток классов типов вокруг XDocument
,
Вы просто пишете файл схемы (Xsd) и включаете его в свой проект (как только вы взломали LinqToXsd в файл csproj:
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Foo1">
<xs:complexType>
<xs:sequence>
<xs:element name="Range1">
<xs:complexType>
<xs:sequence>
<xs:element name="X" type="xs:int"/>
<xs:element name="Y" type="xs:int"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Затем вы можете получить доступ к классам с тем же именем:
Foo1 foo = new Foo1()
{
Range1 = new Foo1.Range1()
{
X = 7,
Y = 10,
}
};
foo.Save(foo.xml);
var input = Foo1.Load(input.xml);
Console.WriteLine(input.Range1.X.ToString());
J.