Измените имя элемента массива xml в сериализованном объекте ASP.NET WebAPI

Я пытаюсь вывести собственный элемент xml root, возвращая список объектов в моем контроллере WebAPI.

Мой метод контроллера выглядит примерно так:

    public List<Product> Get()
    {
        return repository.GetProducts();
    }

который выводит xml-вывод следующим образом:

<ArrayOfProduct>
  <Product>
    <Name>Product1</Name>
  </Product>
  <Product>
    <Name>Product2</Name>
  </Product>
</ArrayOfProduct>

Я хотел бы изменить <ArrayOfProduct> на <Products>, но не нашел способа сделать это.

Я пробовал разные варианты атрибутов DataContract и DataMember безрезультатно.

Кто-нибудь знает, есть ли способ сделать то, что я хочу, за исключением переноса моего объекта List<Product> в новый класс и возвращения вместо этого?

Ответы

Ответ 1

class Program
{
    static void Main(string[] args)
    {
        HttpConfiguration config = new HttpConfiguration();

        DataContractSerializer productsSerializer = new DataContractSerializer(typeof(List<Product>), "Products", String.Empty);
        config.Formatters.XmlFormatter.SetSerializer(typeof(List<Product>), productsSerializer);

        config.Formatters.XmlFormatter.Indent = true;

        config.Formatters.XmlFormatter.WriteToStreamAsync(
            typeof(List<Product>),
            new List<Product> { new Product { Name = "Product1" }, new Product { Name = "Product2" } },
            Console.OpenStandardOutput(),
            null,
            null).Wait();

        Console.WriteLine();
    }
}

[DataContract(Namespace = "")]
public class Product
{
    [DataMember]
    public string Name { get; set; }
}

Ответ 2

Я знаю, что вы не любите идею обертки, но есть решение, которое несколько использует оболочку, но также использует атрибуты xml, с которыми очень легко работать. Мой отказ от использования следующего подхода - использование старого сериализатора.

public class Product
{
    [XmlAttribute( "id" )]
    public int Id
    {
        get;
        set;
    }
    [XmlAttribute( "name" )]
    public string Name
    {
        get;
        set;
    }
    [XmlAttribute( "quantity" )]
    public int Quantity
    {
        get;
        set;
    }
}
[XmlRoot( "Products" )]
public class Products
{
    [XmlAttribute( "nid" )]
    public int Id
    {
        get;
        set;
    }
    [XmlElement(ElementName = "Product")]
    public List<Product> AllProducts { get; set; }
}

Теперь ваш контроллер может просто вернуть такие продукты, как:

    public Products Get()
    {
        return new Products
        {
            AllProducts = new List<Product>
            {
                new Product {Id = 1, Name = "Product1", Quantity = 20},
                new Product {Id = 2, Name = "Product2", Quantity = 37},
                new Product {Id = 3, Name = "Product3", Quantity = 6},
                new Product {Id = 4, Name = "Product4", Quantity = 2},
                new Product {Id = 5, Name = "Product5", Quantity = 50},
            }
        };
    }

теперь вы можете указать сериализатор при запуске следующим образом:

    var productssXmlFormatter = GlobalConfiguration.Configuration.Formatters.XmlFormatter;
    productssXmlFormatter.SetSerializer<Products>( new XmlSerializer( typeof( Products ) ) );

Я знаю, что это не самый идеальный способ указать сериализатор и потеря гибкости и удобства EF и Linq. Или, по крайней мере, нужно вмешаться, а не просто возвращать IEnumerable < > .

Я должен отдать должное следующему сайту, поскольку я впервые узнал об этом с сайта: http://justthisguy.co.uk/outputting-custom-xml-net-web-api/

Это приведет к следующему xml:

<Products nid="0">
    <Product id="1" name="Product1" quantity="20"/>
    <Product id="2" name="Product2" quantity="37"/>
    <Product id="3" name="Product3" quantity="6"/>
    <Product id="4" name="Product4" quantity="2"/>
    <Product id="5" name="Product5" quantity="50"/>
 </Products>

Пожалуйста, не забудьте посмотреть на указанный сайт.

Ответ 3

Как сказал неуловимый....

[XmlArray("Products")]
public List<Product> Get()
{
    return repository.GetProducts();
}

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

[XmlArray("Products")]
public List<Product> Products { 
  get {
    return repository.GetProducts();
  }
}

Это будет перечислять все элементы Product внутри элемента элемента с именем "Продукты".

<Products>
  <Product>
    <Name>Product1</Name>
  </Product>
  <Product>
    <Name>Product2</Name>
  </Product>
</Products>

P.S. Чтобы переименовать теги элементов Product, вы можете использовать атрибут [XmlArrayItem("Product")] для этого. Если вы хотите иметь плоский список (не вставляйте их в "Продукты" ), вы можете использовать [XmlElement("Product")], и они будут перечислять их без их группировки.

Ответ 4

Я предполагаю, что вы используете NetDataContractSerializer
Вы можете указать имя корневого элемента в конструкторе сериализатора:

NetDataContractSerializer serializer = new NetDataContractSerializer("Products", "Your.Namespace");

Взгляните на различные перегрузки:

http://msdn.microsoft.com/en-us/library/system.runtime.serialization.netdatacontractserializer.aspx

Ответ 5

Я столкнулся с той же проблемой и обыскал, но не смог найти правильное решение, поэтому, наконец, решил заменить строку "ArrayOfProduct" на "Продукты", и она работает гладко.

Я знаю, что это плохой способ сделать это, но это быстро.

Ответ 6

Вы должны иметь возможность использовать атрибуты сериализации Xml, как описано здесь

С тех пор я работал с ними, но они были быстрыми и легкими в освоении.

Ответ 7

Я делал это много раз, настраивая вывод сериализации xml. Использование атрибутов в определениях моего класса:

[XmlArray("Products")]
public List<Product> Get()
{
    return repository.GetProducts();
}

это даст вам результат, который вы ищете...