XmlArray с несколькими именами

Я искал часы и нашел похожие результаты, но не для этого конкретного сценария. Рассмотрим следующий 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, но похоже, что это должно быть несложно.

Это помогает? Как определить несколько имен для поля XmlElement?. Это

dbc 10.04.2018 01:34

@dbc - Я видел этот пост до создания этого. Это привело меня к хорошему изучению XmlChoiceIdentifier, но проблема в том, что он работает только с элементами, а не с массивами. В своем эксперименте с ним я создал 3-й класс для элементов largeImages / smallImages, но все равно получил 2 массива. root.images [0] .images и root.images [1] .images.

Brad R. 10.04.2018 20:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
1 020
4
Перейти к ответу Данный вопрос помечен как решенный

Ответы 4

XPATH, вероятно, ваш ответ, если вы действительно не заботитесь о его сопоставлении с классом. Подстановочные знаки XPath в имени узла дает пример того, как вы выбираете несколько элементов - http://csharp.net-tutorials.com/xml/using-xpath-with-the-xmldocument-class/ дает пример того, как это используется в C#.

Другой способ - использовать XSLT: использование кода: Как применить таблицу стилей XSLT в C# и XSLT, например Объединение элементов из 2 списков в XSLT, должно дать вам то, что вы хотите.

Лично я бы выбрал то, что облегчает жизнь, поскольку в этом примере вам не все равно, что такое промежуточная структура данных.

Спасибо, киборг. Я надеялся найти простой способ управлять им в процессе десериализации с помощью атрибутов Xml. Если это окажется невозможным, идея преобразования этого с помощью XSLT может сработать.

Brad R. 10.04.2018 20:39

Простой!!! Я делаю это все время. Уловка заключается в том, что с 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 и получить единый массив, содержащий оба набора изображений.

Brad R. 10.04.2018 20:42

Измените код, чтобы использовать List <> вместо массива, чтобы я мог добавлять изображения обоих типов в один и тот же объект.

jdweng 10.04.2018 21:25

Это действительно загружает все изображения в один список. Я думаю, что это может быть хорошим выбором для простой модели данных, такой как мой образец. Я думаю, что для более сложной модели данных с десятками свойств лучше использовать XmlSerializer.

Brad R. 11.04.2018 00:10
Ответ принят как подходящий

Учитывая, что ответ от Как определить несколько имен для поля 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, заставляет компилятор использовать одну и ту же базовую переменную для обоих свойств?

Brad R. 11.04.2018 00:28

@BradR. - Да. SmallImagesSurrogate просто возвращает значение свойства Images и поэтому не имеет собственного поля поддержки. Вот еще один пример: Являются ли «прокси-свойства» хорошим стилем?. См. Также Автоматические и явные свойства и В чем разница между полем и свойством?.

dbc 11.04.2018 00:41

У меня была проблема с сериализацией модели, и я нашел ответ благодаря 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>

Другие вопросы по теме