HttpResponseMessage сериализуется в xml - PullRequest
0 голосов
/ 12 февраля 2020

В нашем приложении у нас есть REST-Api с несколькими контроллерами, такими как UsersController или UserGroupsController и многими другими. Все вызовы (get, put, pu sh, delete) на каждой REST-конечной точке всегда возвращают HttpResponseMessage, который всегда является объектом следующего типа:

public class ComRestResult<T> where T : class
{
   public T Data { get; set; }

   public ValidationItem[] ValidationItems { get; set; }
}

И ValidationItem - класс выглядит так:

[DataContract]
[Serializable]
public class ValidationItem
{
   [DataMember]
   public string[] ErrorParameters { get; set; }

   [DataMember]
   public ValidationSeverity Severity { get; set; }

   private ValidationId validationId;

   [IgnoreDataMember]
   [XmlIgnore]
   public ValidationId ValidationId
   {
      get { return validationId; }
      set
      {
         validationId = value;
         if(validationId != ValidationId.UnresolvedError)
         {
            legacyValidationId = validationId.ToString();
         }
      }
   }

   private string legacyValidationId;

   [DataMember(Name = "ValidationId")]
   [XmlElement("ValidationId")]
   public string LegacyValidationId
   {
      get { return legacyValidationId; }
      set
      {
         legacyValidationId = value;
         validationId = ValidationId.UnresolvedError;
         ValidationId parsedValidationId;
         if(!string.IsNullOrEmpty(legacyValidationId) && Enum.TryParse(legacyValidationId, out parsedValidationId))
         {
            validationId = parsedValidationId;
         }
      }

ValidationId и Severity являются перечислениями.

Например, Get-Method для UsersController выглядит следующим образом:

[HttpGet]
[Route("")] 
public HttpResponseMessage Get()
{
    RequestHandler handler = new RequestHandler(Request);
    HttpResponseMessage responseMessage = handler.ProcessRequest<User[]>(PersonsAccess.GetAllPersons);
    return responseMessage;
}

RequestHandler создает необходимый HttpResponseMessage и принимает Func<object, T> в своем ProcessRequest-методе.

HttpResponseMessage создается внутри ProcessRequest-метода со следующей строкой:

HttpResponseMessage httpResponseMessage = request.CreateResponse(httpStatusCode, restResult);

Все просто отлично работает.

Если я посмотрю на ответ в json, он выглядит так:

{"ValidationItems":[{"ValidationId":"UserIsNew","ErrorParameters":["foo","bar"],"Severity":0,}],"Data":[{"UserId":"ABC123","UserName":"John Smith","IsNew":true},{"UserId":"XYZ789","UserName":"Raimond Test","IsNew":true}]}

Это выглядит хорошо. Теперь, если я посмотрю на ответ как xml, он выглядит так:

<ComRestResult xmlns:i="http://www.w3.org/2001/XMLSchema-instance">
  <Data xmlns:d2p1="http://schemas.datacontract.org/2004/07/Comp.Rest.Data.Persons">
    <d2p1:User>
      <d2p1:UserId>ABC123</d2p1:UserId>
      <d2p1:UserName>John Smith</d2p1:UserName>
      <d2p1:IsNew>true</d2p1:IsNew>
    </d2p1:User>
    <d2p1:User>
      <d2p1:UserId>XYZ789</d2p1:UserId>
      <d2p1:UserName>Raimond Test</d2p1:UserName>
      <d2p1:IsNew>true</d2p1:IsNew>
    </d2p1:User>
  </Data>
  <ValidationItems xmlns:d2p1="http://schemas.datacontract.org/2004/07/Comp.BaseInterface.Validation">
    <d2p1:ValidationItem>
      <d2p1:ValidationId>UserIsNew</d2p1:ValidationId>
      <d2p1:ErrorParameters xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
        <d4p1:string>foo</d4p1:string>
        <d4p1:string>bar</d4p1:string>
      </d2p1:ErrorParameters>
      <d2p1:Severity>Information</d2p1:Severity>
    </d2p1:ValidationItem>
  </ValidationItems>
</ComRestResult>

И это выглядит не очень хорошо. Хотя вся информация присутствует, у нас есть проблема с пространствами xml -names. d2p1 и все остальные.

Если я применяю атрибуты [DataContract] и [DataMember] к классу User, например:

[DataContract(Name = "User", Namespace = "")]
public class User
{
    [DataMember(Name = "UserId")]
    public string UserId { get; set; }
    [DataMember(Name = "UserName")]
    public string UserName { get; set; }
    [DataMember(Name = "IsNew")]
    public bool IsNew { get; set; }
}

ответ- xml для пользователя выглядит так:

<Data>
 <User>
  <IsNew>true</IsNew>
  <UserId>ABC123</UserId>
  <UserName>John Smith</UserName>
 </User>
 <User>
  <IsNew>true</IsNew>
  <UserId>XYZ789</UserId>
  <UserName>Raimond Test</UserName>
 </User>
</Data>

Это то, что нам нужно. Но действительно ли нам нужно применять атрибуты к каждому классу, или есть возможность деактивировать (или около того) пространство имен при сериализации в целом?

Другая проблема - string[] в ValidationItem - учебный класс. На данный момент это сериализовано как:

<d2p1:ErrorParameters xmlns:d4p1="http://schemas.microsoft.com/2003/10/Serialization/Arrays">
    <d4p1:string>foo</d4p1:string>
    <d4p1:string>bar</d4p1:string>
</d2p1:ErrorParameters>

Я знаю, что могу заменить string[] следующим классом:

[CollectionDataContract(ItemName = "value", Namespace = "")]
public class StringArray : List<string>
{
}

Но, к сожалению, этот класс очень старый, очень часто используется (~ 4000 ссылок), а также используется для SOAP -сериализации. Поэтому мы не можем легко изменить это.

Там нам также нужно что-то вроде обобщенной c сериализации на xml без пространств имен.

1 Ответ

0 голосов
/ 12 февраля 2020

Я решил это сам.

Согласно следующей статье можно использовать XmlSerializer вместо DataContractSerializer. Это просто отлично работает.

https://docs.microsoft.com/de-de/aspnet/web-api/overview/formats-and-model-binding/json-and-xml-serialization#xml_media_type_formatter

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...