RestSharp方法引发System.Xml.XMlException“ =”是意外令牌。预期令牌为“;”

Sid*_*idC 8 c# json json.net restsharp

我查看了该问题的答案,发现无效字符会导致引发此错误的问题。我的问题有点不同,我使用RestSharp进行API调用,如下所示:

 private static T Execute<T>(IRestRequest request, string baseUrl) where T : class, new()
    {
        var client = new RestClient(baseUrl);
        var response = client.Execute<T>(request);

        if (response.ErrorException != null)
        {
            Console.WriteLine(
                "Error: Exception: {0}, Headers: {1}, Content: {2}, Status Code: {3}",
                response.ErrorException,
                response.Headers,
                response.Content,
                response.StatusCode);
        }

        return response.Data;
    }

 public static ProPayResponse MerchantSignUpForProPay()
    {
        var baseUrl = "https://xmltestapi.propay.com/ProPayAPI";
        var request = BuildMerchantTestData();
        var restRequest = CreateRestRequest("SignUp", Method.PUT);
        restRequest.AddJsonBody(request);
        return Execute<ProPayResponse>(restRequest, baseUrl);
    }

    private static async Task<RestRequest> CreateRestRequest(string resource, Method method)
    {

        var credentials = GetCredentials();

        var restRequest = new RestRequest { Resource = resource, Method = method, RequestFormat = DataFormat.Json, };
        restRequest.AddHeader("accept", "application/json");
        restRequest.AddHeader("Authorization", credentials);
        return restRequest;
    }
private static string GetCredentials()
    {
        var termId = "myterm"; // put affiliate term id here, if you have it
        var certString = "mycertString"; // put affiliate cert string here
        var encodedCredentials = Convert.ToBase64String(Encoding.Default.GetBytes(certString + ":" + termId));

        var credentials = $"Basic {encodedCredentials}";
        return credentials;
    }
Run Code Online (Sandbox Code Playgroud)

异常的完整堆栈跟踪如下:

Error: Exception: System.Xml.XmlException: '=' is an unexpected token. The expected token is ';'. Line 26, position 43.
 at System.Xml.XmlTextReaderImpl.Throw(Exception e)
 at System.Xml.XmlTextReaderImpl.Throw(String res, String[] args)
 at System.Xml.XmlTextReaderImpl.ThrowUnexpectedToken(String expectedToken1, String expectedToken2)
 at System.Xml.XmlTextReaderImpl.HandleEntityReference(Boolean isInAttributeValue, EntityExpandType expandType, Int32& charRefEndPos)
 at System.Xml.XmlTextReaderImpl.ParseText(Int32& startPos, Int32& endPos, Int32& outOrChars)
 at System.Xml.XmlTextReaderImpl.FinishPartialValue()
 at System.Xml.XmlTextReaderImpl.get_Value()
 at System.Xml.Linq.XContainer.ContentReader.ReadContentFrom(XContainer rootContainer, XmlReader r)
 at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r)
 at System.Xml.Linq.XContainer.ReadContentFrom(XmlReader r, LoadOptions o)
 at System.Xml.Linq.XDocument.Load(XmlReader reader, LoadOptions options)
 at System.Xml.Linq.XDocument.Parse(String text, LoadOptions options)
 at RestSharp.Deserializers.XmlDeserializer.Deserialize[T](IRestResponse response)
 at RestSharp.RestClient.Deserialize[T](IRestRequest request, IRestResponse raw), Headers: System.Collections.Generic.List`1[RestSharp.Parameter], Content:
Run Code Online (Sandbox Code Playgroud)

运行此代码时,我确实注意到在堆栈跟踪的内容部分中引发了HTTP 404。

我认为这意味着我有一个错误,baseURl但不确定,并想知道是否是这种情况,或者我的代码是否还有其他问题?

更新: 在进一步研究了这个问题之后,我认为引发了错误,因为在发送之前我没有将模型对象序列化为JSON RestRequest

发出请求之前,我需要序列化所有对象吗?

更新2: 由于第二眼,我更正了URL。现在,当我运行我的应用程序时,将引发以下错误:

Error: Exception: System.Xml.XmlException: Data at the root level is invalid. Line 1, position 1.
at System.Xml.XmlTextReaderImpl.Throw(Exception e)
at System.Xml.XmlTextReaderImpl.Throw(String res, String arg)
at System.Xml.XmlTextReaderImpl.ParseRootLevelWhitespace()
at System.Xml.XmlTextReaderImpl.ParseDocumentContent()
at System.Xml.XmlTextReaderImpl.Read()
at System.Xml.Linq.XDocument.Load(XmlReader reader, LoadOptions options)
at System.Xml.Linq.XDocument.Parse(String text, LoadOptions options)
at RestSharp.Deserializers.XmlDeserializer.Deserialize[T](IRestResponse response)
at RestSharp.RestClient.Deserialize[T](IRestRequest request, IRestResponse raw), Message: Data at the root level is invalid. Line 1, position 1., Headers: System.Collections.Generic.List`1[RestSharp.Parameter], Content: ?<?xml version="1.0" encoding="utf-8"?>
Run Code Online (Sandbox Code Playgroud)

NZg*_*eek 2

在更新 2 之后,RestSharp 似乎在 XML 的开头引入了一个意外的字符。

这是来自错误消息:

Content: ?<?xml version="1.0" encoding="utf-8"?>
Run Code Online (Sandbox Code Playgroud)

前面的问号<?xml就是问题所在。它不是 XML 的有效字符,并且会导致 XML 解析器抛出错误。

我的最佳猜测是响应中的 XML 内容开头有一个 UTF-8 字节顺序标记 (BOM)。从技术上讲,BOM 不是有效的字符,您的日志记录代码/框架正在将其转换为?用于显示的字符。

您可以通过调用.ExecuteTaskAsync(request)而不是.ExecuteTaskAsync<T>(request)查看返回的数据来测试这一点response.RawBytes。如果返回的前 3 个字节是,0xEF 0xBB 0xBF则您的响应中包含 BOM。

快速解决

这应该可以完成工作,并且需要最少的代码更改。

restRequest.OnBeforeDeserialization = resp => {
    if (resp.RawBytes.Length >= 3 && resp.RawBytes[0] == 0xEF && resp.RawBytes[1] == 0xBB && resp.RawBytes[2] == 0xBF)
    {
        // Copy the data but with the UTF-8 BOM removed.
        var newData = new byte[resp.RawBytes.Length - 3];
        Buffer.BlockCopy(resp.RawBytes, 3, newData, 0, newData.Length);
        resp.RawBytes = newData;

        // Force re-conversion to string on next access
        resp.Content = null;
    }
};
Run Code Online (Sandbox Code Playgroud)

这将确保尽早删除 BOM。当它转换为字符串进行 XML 解析时,BOM 将不存在。

更长的修复

您可以为 XML 创建自己的反序列化器,它会检测 XML 开头的 BOM 并在解析之前将其删除。这里的步骤是:

  1. 子类RestSharp.Deserializers.XmlDeserializer. 这将需要一个方法重写:
public override T Deserialize<T>(IRestResponse response)
{
    if (string.IsNullOrEmpty(response.Content))
        return default(T);

    if (response.Content[0] == '\uFEFF')
        response.Content = response.Content.Substring(1);

    return base.Deserialize<T>(response);
}
Run Code Online (Sandbox Code Playgroud)
  1. 创建上述类的实例。
  2. 创建上述步骤 2 中的类的实例RestSharp.Deserializers.XmlRestSerializer并调用该类。.WithXmlDeserializer()
  3. 调用.AddHandler("application/xml", () => xmlRestSerializer)您的RestClient实例。
    • xmlRestSerializer是您在上面第 3 步中创建的对象。
    • 您可能需要替换application/xml为其他内容,具体取决于 REST API 返回的内容。