Я искал часы и нашел похожие результаты, но не для этого конкретного сценария. Рассмотрим следующий XML-файл.
<root>
<largeImages>
<largeImage>
<url>./imageLarge.jpg</url>
<height>480</height>
<width>640</width>
</largeImage>
</largeImages>
<smallImages>
<smallImage>
<url>./imageSmall.jpg</url>
<height>240</height>
<width>320</width>
</smallImage>
</smallImages>
</root>
Я пытаюсь десериализовать это в один массив изображений вместо двух массивов.
public class root {
[XmlArray("largeImages")]
[XmlArrayItem("largeImage")]
public image[] largeImages { get; set; }
[XmlArray("smallImages")]
[XmlArrayItem("smallImage")]
public image[] smallImages { get; set; }
}
Этот класс дает мне 2 массива. root.largeImages и root.smallImages. Поскольку мое приложение не заботится о больших или маленьких изображениях, я хотел бы десериализовать их в один массив root.images. Я безуспешно пробовал варианты XmlArray, XmlArrayItem, XmlElement и даже XmlChoiceIdentifier. Я думаю о чем-то вроде следующего, что не будет компилироваться, потому что, по-видимому, XmlArrayAttribute можно использовать только один раз для каждого свойства.
[XmlArray("largeImages")]
[XmlArray("smallImages")]
[XmlArrayItem("largeImage")]
[XmlArrayItem("smallImage")]
public image[] images { get; set; }
Очевидно, я мог бы объединить 2 массива в коде после десериализации XML, но похоже, что это должно быть несложно.
@dbc - Я видел этот пост до создания этого. Это привело меня к хорошему изучению XmlChoiceIdentifier, но проблема в том, что он работает только с элементами, а не с массивами. В своем эксперименте с ним я создал 3-й класс для элементов largeImages / smallImages, но все равно получил 2 массива. root.images [0] .images и root.images [1] .images.





XPATH, вероятно, ваш ответ, если вы действительно не заботитесь о его сопоставлении с классом. Подстановочные знаки XPath в имени узла дает пример того, как вы выбираете несколько элементов - http://csharp.net-tutorials.com/xml/using-xpath-with-the-xmldocument-class/ дает пример того, как это используется в C#.
Другой способ - использовать XSLT: использование кода: Как применить таблицу стилей XSLT в C# и XSLT, например Объединение элементов из 2 списков в XSLT, должно дать вам то, что вы хотите.
Лично я бы выбрал то, что облегчает жизнь, поскольку в этом примере вам не все равно, что такое промежуточная структура данных.
Спасибо, киборг. Я надеялся найти простой способ управлять им в процессе десериализации с помощью атрибутов Xml. Если это окажется невозможным, идея преобразования этого с помощью XSLT может сработать.
Простой!!! Я делаю это все время. Уловка заключается в том, что с root вам нужно использовать Elements (), а затем использовать FirstOrDefault (), чтобы получить только один.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
namespace ConsoleApplication1
{
class Program
{
const string FILENAME = @"c:\temp\test.xml";
static void Main(string[] args)
{
XDocument doc = XDocument.Load(FILENAME);
Root root = doc.Elements("root").Select(x => new Root() {
Images = x.Descendants("largeImage").Select(z => new Image() {
url = (string)z.Element("url"),
height = (int)z.Element("height"),
width = (int)z.Element("width")
}).ToList()
}).FirstOrDefault();
root.Images.AddRange(doc.Descendants("smallImage").Select(z => new Image() {
url = (string)z.Element("url"),
height = (int)z.Element("height"),
width = (int)z.Element("width")
}).ToList());
}
}
public class Root
{
public List<Image> Images { get; set; }
}
public class Image
{
public string url { get; set; }
public int height { get; set; }
public int width { get; set; }
}
}
Спасибо, jdweng. Однако результат остается таким же, как при использовании XmlSerializer с моим исходным классом. Цель состоит в том, чтобы использовать атрибуты XmlSerializer и Xml и получить единый массив, содержащий оба набора изображений.
Измените код, чтобы использовать List <> вместо массива, чтобы я мог добавлять изображения обоих типов в один и тот же объект.
Это действительно загружает все изображения в один список. Я думаю, что это может быть хорошим выбором для простой модели данных, такой как мой образец. Я думаю, что для более сложной модели данных с десятками свойств лучше использовать XmlSerializer.
Учитывая, что ответ от Как определить несколько имен для поля XmlElement? работает в первую очередь для элементов, а не для массивов, самым простым решением могло бы быть введение суррогатного свойства для одного из элементов изображения, скажем, <smallImages>:
public class root
{
[XmlArray("largeImages")]
[XmlArrayItem("largeImage")]
public List<image> Images { get; set; } = new List<image>();
[XmlArray("smallImages")]
[XmlArrayItem("smallImage")]
[Browsable(false), EditorBrowsable(EditorBrowsableState.Never), DebuggerBrowsable(DebuggerBrowsableState.Never)]
public List<image> SmallImagesSurrogate { get { return Images; } }
public bool ShouldSerializeSmallImagesSurrogate() { return false; }
}
Заметки:
При десериализации в предварительно выделенный List<T>, XmlSerializer просто десериализует элементы добавить в существующий список. Это позволяет объединять элементы как из <largeImages>, так и из <smallImages> в одну коллекцию без потери данных.
Суррогатное свойство должно быть общедоступным, но его можно сделать «менее видимым», установив такие атрибуты, как [Browsable(false)].
XmlSerializer может успешно десериализовать свойства коллекции только для получения, если коллекция предварительно выделена. Я воспользовался этим, чтобы не добавлять сеттер для суррогата.
Если вам нужно повторно сериализовать root, метод ShouldSerializeSmallImagesSurrogate() предотвратит двойную сериализацию массива изображений. (Для объяснения причин см. ShouldSerialize * () vs * Указанный шаблон условной сериализации.) Вместо этого все образы будут сериализованы под <largeImages>. Этот метод не влияет на десериализацию.
Образец рабочий .Net рабочий пример.
Это блестящее решение, которое, кажется, делает именно то, что нужно. Я не понимаю, почему smallImages десериализуется в Images вместо SmallImagesSurrogate. Это потому, что использование только геттера, который возвращает свойство Images, заставляет компилятор использовать одну и ту же базовую переменную для обоих свойств?
@BradR. - Да. SmallImagesSurrogate просто возвращает значение свойства Images и поэтому не имеет собственного поля поддержки. Вот еще один пример: Являются ли «прокси-свойства» хорошим стилем?. См. Также Автоматические и явные свойства и В чем разница между полем и свойством?.
У меня была проблема с сериализацией модели, и я нашел ответ благодаря MSDN. Вот мое решение:
public class Document
{
[XmlElement(ElementName = "seller_id")]
public string SellerId { get; set; }
[XmlArray(ElementName = "order_details")]
[XmlArrayItem(Type = typeof(SgtinCode), ElementName = "sgtin")]
[XmlArrayItem(Type = typeof(SsccCode), ElementName = "sscc")]
public Code[] Codes { get; set; }
}
public abstract class Code
{
[XmlText]
public string Value { get; set; }
}
public class SgtinCode : Code
{ }
public class SsccCode : Code
{ }
Настройка модели:
var document = new Document
{
SellerId = Guid.NewGuid().ToString(),
Codes = new Code[]
{
new SsccCode { Value = "111700126101510000000000011" },
new SsccCode { Value = "111700126101510000000000012" },
new SsccCode { Value = "111700126101510000000000013" },
new SgtinCode { Value = "abc" }
}
}
И вывод XML:
<?xml version = "1.0" encoding = "utf-8"?>
<documents>
<foreign_shipment>
<seller_id>fb2d35e7-c5d1-43ad-a272-89f897f41058</seller_id>
<order_details>
<sscc>111700126101510000000000011</sscc>
<sscc>111700126101510000000000012</sscc>
<sscc>111700126101510000000000013</sscc>
<sgtin>abc</sgtin>
</order_details>
</foreign_shipment>
</documents>
Это помогает? Как определить несколько имен для поля XmlElement?. Это