【发布时间】:2022-01-06 17:37:58
【问题描述】:
我正在使用 REST API,它为同一请求返回 2 种不同类型的 XML 响应。
例如,如果我使用某个票号请求一张票,例如对该 API 说 12345,它会返回:
- 门票:
- 或者说没有票:
(由于某种原因我无法格式化我的 XML,所以只是粘贴了屏幕截图。)
请注意,两种情况下的状态码都是Ok。我知道这是一个糟糕的 api 设计,但我们无法对其进行任何更改。
在JSON2Csharp 网站的帮助下,我想出了这些类来表示响应:
Ticket 类:
[XmlRoot(ElementName = "Tickets")]
public class TicketsResponse
{
public List<Ticket> Tickets { get; set; } = new List<Ticket>();
public bool HasTickets() => Tickets.Any();
}
[XmlRoot(ElementName = "Ticket")]
public class Ticket
{
[XmlElement(ElementName = "Field1", IsNullable = true)]
public string Field1 { get; set; }
public bool ShouldSerializeField1() { return Field1 != null; }
[XmlElement(ElementName = "TicketNumber")]
public int TicketNumber { get; set; }
[XmlElement(ElementName = "SomeOtherDetails")]
public SomeOtherDetails SomeOtherDetails { get; set; }
[XmlElement(ElementName = "Accessorials")]
public object Accessorials { get; set; }
}
[XmlRoot(ElementName = "SomeOtherDetails")]
public class SomeOtherDetails
{
[XmlElement(ElementName = "SomeOtherField1", IsNullable = true)]
public string SomeOtherField1 { get; set; }
public bool ShouldSerializeSomeOtherField1() { return SomeOtherField1 != null; }
}
错误类:
[XmlRoot(ElementName = "response")]
public class ErrorResponse
{
public byte requestId { get; set; }
public byte errorCode { get; set; }
public string errorDesc { get; set; }
public ErrorResponseBody body { get; set; }
public bool HasErrors()
{
var hasTopLevelError = errorCode != 0;
var hasErrorBody = body?.errors?.Any() ?? false;
if (hasTopLevelError || hasErrorBody)
{
return true;
}
return false;
}
public string ErrorMessage()
{
var hasTopLevelError = errorCode != 0;
var hasErrorBody = body?.errors?.Any() ?? false;
if (hasTopLevelError)
{
return errorDesc;
}
else if (hasErrorBody)
{
return string.Join(", ", body.errors.Select(e => e.errorDescription));
}
return null;
}
}
[XmlRoot(ElementName = "body")]
public class ErrorResponseBody
{
[XmlElement("errors")]
public List<Error> errors { get; set; }
}
[XmlRoot(ElementName = "Error")]
public class Error
{
public byte errorId { get; set; }
public string errorDescription { get; set; }
public string errorObjectId { get; set; }
}
然后我使用存在的TicketNumber 调用API。
我正在使用RestSharp 调用 api:
public async void SendRequestAndReceiveResponse()
{
var restClient = new RestClient("https://someapiaddress.net");
var requestXMLBody = "<request><request_id>1</request_id><operation>retrieve</operation><method /><entity>ticket</entity><user>someuser</user><password>somepassword</password><body><ticket><TicketNumber>12345</TicketNumber></ticket></body></request>";
var request = new RestRequest("somexmlwebservice!process.action", Method.POST);
request.AddParameter("xmlRequest", requestXMLBody, "text/xml", ParameterType.QueryString);
var response = await restClient.ExecuteAsync<TicketsResponse>(request);
// Do other stuffs with this response...
}
现在效果很好。因为我知道我的回复会收到票,并且会正确反序列化为 TicketsResponse 对象。
但是,如果我使用不存在的 TicketNumber 调用 API,我只会得到 TicketsResponse 对象,其中包含 Tickets 的空列表,因为这次我收到错误响应。在这种情况下,状态码也变为OK。
我在这里要做的是我想从错误响应中捕获错误消息。 (Ticket 或 Error 的响应也适用于许多其他进程,因此在一次调用中获取此信息很重要。)
如果我知道这张票不存在,我可以简单地以这种方式调用 API 并捕获 errors。但这并不理想,甚至不是一个好主意:
var response = await restClient.ExecuteAsync<ErrorResponse>(request);
所以我想到了将TicketsResponse和ErrorResponse结合起来,像这样:
[XmlRoot]
public class CombinedResponse
{
[XmlElement(ElementName = "Tickets")]
public TicketsResponse Data { get; set; }
[XmlElement(ElementName = "response")]
public ErrorResponse NonData { get; set; }
}
并使用该类获取响应:
var response = await restClient.ExecuteAsync<CombinedResponse>(request);
状态码来自OK(当它返回数据或错误消息时),我在response.Content 中得到正确响应,但反序列化不起作用,所以我的response.Data 将显示2 个字段@987654346 @ 和 NonData 都是 null。理想情况下,我应该在response.Data 中获得我的Ticket 数据或Error 数据。
所以我的问题是:
是否可以使用单个类进行反序列化来完成这项工作?
我在这方面花费了太多时间,因此不胜感激。 另外请查看我的模型类并建议是否有更好的处理方式。
【问题讨论】:
-
您能否说明如何您正在进行 http 调用并解析响应? HTTP 状态码是否随着返回的 XML 模式而改变?
-
您可以通过将所有这些字段和属性组合到一个类中来创建一个 C# 类。我很好奇你为什么只想要1节课?此外,如果您使用 Visual Studio,您可以复制 XML 数据,并使用菜单编辑 -> 选择性粘贴 -> 将 XML 粘贴为类。
-
尝试使用控制器。它可以处理多种类型的响应并将结果放入您在上面发布的类中:docs.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/…
-
@gunr2171 抱歉,我回复晚了。我只是将到目前为止我所做的所有事情都添加到了原始问题中。请看一看。
-
@thewallrus 我尝试将它们组合成一个类。而且我还更新了我的问题,说明我为什么想要这个。而且我不喜欢将 XML 粘贴为 Visual Studio 中的类,因为它生成的类的属性不是自动实现的(至少这是我的经验,也许我做错了)。
标签: c# .net xml-parsing .net-5 restsharp