API .NET Core 3.1 для приема сложных XML-объектов

#c# #xml #api #asp.net-core-3.1 #datacontractserializer

Вопрос:


Редактировать

Ввод XML будет выглядеть следующим образом

 <?xml version="1.0" encoding="UTF-8"?>
<Object1>
   <field1>Hello</field1>
   <field2>
      <field3>World</field3>
      <field4>
         <field5>Test</field5>
         <field6>Test2</field6>
      </field4>
   </field2>
</Object1>
 

Я немного зациклился на том, как заставить мой API принимать сложный XML-объект

Например, у меня есть такой класс

 public class Object1
{
 public string field1 {get; set;}
 public Object2 field2 {get; set;}
}

public class Object2
{
 public string field3 {get; set;}
 public Object3 field4 {get; set;}
}

public class Object3
{
 public string field5 {get; set;}
 public string field6 {get; set;}
}
 

В моем файле startup.cs я добавил это

 services.AddControllers().AddXmlDataContractSerializerFormatters()
               .AddXmlSerializerFormatters();
 

Затем я получил сообщение об ошибке, что он не смог десериализовать ввод.

Поэтому я добавил эти атрибуты к объекту

 [DataContract(Namespace = "")]
[XmlRoot]
public class Object1
{
 [DataMember(Name = "field1")]
 public string field1 {get; set;}
 [DataMember(Name = "field2")]
 public Object2 field2 {get; set;}
}

[DataContract(Name = "field2", IsReference = true)]
public class Object2
{
 [DataMember(Name = "field3")]
 public string field3 {get; set;}
 [DataMember(Name = "field4")]
 public Object3 field4 {get; set;}
}

[DataContract(Name = "field3", IsReference = true)]
public class Object3
{
 [DataMember(Name = "field5")]
 public string field5 {get; set;}
 [DataMember(Name = "field6")]
 public string field6 {get; set;}
}
 

Так что теперь, когда я отправляю XML-объект, над ним можно немного поработать. Я могу получить значение field1, и похоже, что объект field2 больше не является нулевым, но все внутри него равно нулю.

Я не уверен, как подойти к этому правильно. Чего-нибудь мне не хватает?

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

 [HttpPost]
[Consumes("application/xml")]
public async Task<IActionResult> UpdateObject([FromBody]Object1 object1)
{
  var testObject1 = object1.field1; // this value is not null because it is a string datatype and not like the one below which is slightly more complex
  var testObject2 = object1.field2; //this is not null but the properties inside the object are null
}
 

Ответ №1:

Вам нужно установить DataContractAttribute.Namespace для Object2 и Object3 также:

 [DataContract(Namespace = "", Name = "field2", IsReference = true)]
public class Object2
{
 [DataMember(Name = "field3")]
 public string field3 {get; set;}
 [DataMember(Name = "field4")]
 public Object3 field4 {get; set;}
}

[DataContract(Namespace = "", Name = "field3", IsReference = true)]
public class Object3
{
 [DataMember(Name = "field5")]
 public string field5 {get; set;}
 [DataMember(Name = "field6")]
 public string field6 {get; set;}
}
 

Демонстрационная скрипка №1 здесь.

Примечания:

  • С помощью сериализатора контрактов на данные, если вы явно не указываете пространство имен для объекта контракта на данные, назначается значение по умолчанию, как описано в документах:

    По умолчанию контрактам данных для определенного типа назначается пространство имен, которое происходит из пространства имен среды CLR этого типа.

    По умолчанию любое заданное пространство имен CLR (в формате Clr.Пространство имен) сопоставляется с пространством http://schemas.datacontract.org/2004/07/Clr.Namespace имен .

  • Логика пространства имен по умолчанию DataContractSerializer отличается от XmlSerializer . Объекты по умолчанию не назначаются пространству XmlSerializer имен , поэтому ваша исходная модель работает как есть с этим сериализатором. Демонстрационная скрипка №2 здесь.

    Если бы вы предпочли использовать XmlSerializer вместо DataContractSerializer «Я верю», вы могли бы удалить AddXmlDataContractSerializerFormatters() только «оставить AddXmlSerializerFormatters() «.

  • Простой способ отладить проблемы с десериализациейсериализовать вашу модель и сравнить фактические результаты с контентом, который вы пытаетесь десериализовать. Если я попытаюсь сериализовать экземпляр вашей текущей Object1 модели, я получу:
     <?xml version="1.0" encoding="utf-16"?>
    <Object1 xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
      <field1>Hello</field1>
      <field2 xmlns:d2p1="http://schemas.datacontract.org/2004/07/" z:Id="i1" xmlns:z="http://schemas.microsoft.com/2003/10/Serialization/">
        <d2p1:field3>World</d2p1:field3>
        <d2p1:field4 z:Id="i2">
          <d2p1:field5>Test</d2p1:field5>
          <d2p1:field6>Test2</d2p1:field6>
        </d2p1:field4>
      </field2>
    </Object1>    
     

    Из чего видно, что пространство d2p1: имен для элементов Object2 и Object3 неверно. (Точное выбранное здесь пространство http://schemas.datacontract.org/2004/07/ имен будет зависеть от пространства имен CLR вашей модели, которое не указано в вашем вопросе.)

    Демонстрационная скрипка №3 здесь.

  • Ваши XML-элементы не имеют z:Id="xxx" z:ref="xxx" атрибутов или, поэтому я не вижу необходимости включать IsReference механизм отслеживания ссылок.

Комментарии:

1. Спасибо вам за этот вклад! Я не знал, что мне нужно также указать пространство имен для всех других объектов! Большое вам спасибо за это разъяснение!