在发布模式下,NewtonSoft 不会将 json 反序列化为对象
NewtonSoft is not deserializing json to object when in release mode
我的 Android Xamarin Forms 应用程序遇到了一个奇怪的错误。无法找到它的根本原因。以下是我的代码,它获取 API 响应并将其反序列化为一个对象。
public async static Task<T2> ExecuteRequest<T1, T2>(T1 t1, EndPoint endPoint, bool getParams = false, HttpVerb method = HttpVerb.POST)
{
string resStr = "";
try
{
object data = t1;
if (endPoint.ToString() != EndPoints.Login.ToString() && getParams == false)
data = Newtonsoft.Json.JsonConvert.SerializeObject(t1);
HttpUtils.RestClient restClient = new HttpUtils.RestClient(APIPath + endPoint, HttpVerb.POST, data.ToString());
restClient.ContentType = "application/json";
restClient.Method = method;
resStr = await (endPoint != EndPoints.Login && getParams == false ? restClient.MakeRequest() : restClient.MakeRequest(data.ToString()));
if (string.IsNullOrEmpty(resStr))
return default(T2);
return Newtonsoft.Json.JsonConvert.DeserializeObject<T2>(resStr);
}
catch (Newtonsoft.Json.JsonSerializationException e)
{
Device.BeginInvokeOnMainThread(async () => await Application.Current.MainPage.DisplayAlert("Error", e.Message + "\n----------\n" + resStr, "OK"));
return default(T2);
}
catch (System.Net.WebException)
{
Device.BeginInvokeOnMainThread(async () => await Application.Current.MainPage.DisplayAlert("Not Connected", "Please check your Internet connection and restart the app.", "OK"));
return default(T2);
}
}
以下是我尝试使用上述方法登录后端系统的方式:
Services.API.ResponseModels.Login res = await APIAccess.ExecuteRequest<string, Services.API.ResponseModels.Login>(req, APIAccess.EndPoints.Login);
对象Class:
public class Login
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string userName { get; set; }
public string issued { get; set; }
public string expires { get; set; }
}
我正确收到了以下 JSON 格式的回复:
{
"access_token": "<access token received here>",
"token_type": "bearer",
"expires_in": 2591999,
"userName": "<userName received here>",
".issued": "Sun, 16 Jan 2022 09:41:06 GMT",
".expires": "Tue, 15 Feb 2022 09:41:06 GMT"
}
但在调试模式下,NewtonSoft 正确反序列化它并将用户带到另一个屏幕,而在发布模式下,它显示以下错误:
我尝试正常添加默认构造函数并使用 [JsonConstructor]
属性,但它不起作用。我清理并删除了所有项目中的 Bin 和 Obj,还更新了 NewtonSoft JSON 包。在 Release 模式下似乎没有任何效果。
那么为什么你不使用新的 T2() 而不是默认的 (T2) 来尝试呢?
为此,您需要在定义操作时定义 new(),class。
这是一个 linker 问题,您需要提示它不应删除该构造函数,因为它永远不会被直接调用。
一个技巧是创建一个 class,类似 LinkerPleaseInclude
的东西,并创建提示通常会被 link 淘汰的东西的用法的方法。确保告诉编译器不要 link 使用 [Preserve]
属性去掉这个 class。
这可能看起来像:
[Preserve(AllMembers = true)]
public class LinkerPleaseInclude
{
public void Include(Login login)
{
login = new Login();
}
}
然后对所有需要它的类型执行此操作。
您可以将 PreserveAttribute
添加到您的 class 图书馆:
public sealed class PreserveAttribute : Attribute
{
public bool AllMembers;
public bool Conditional;
public PreserveAttribute (bool allMembers, bool conditional)
{
AllMembers = allMembers;
Conditional = conditional;
}
public PreserveAttribute ()
{
}
}
然后在您的 JSON model/class:
上使用该属性
[Preserve(AllMembers = true)]
public class Login
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string userName { get; set; }
public string issued { get; set; }
public string expires { get; set; }
}
我的 Android Xamarin Forms 应用程序遇到了一个奇怪的错误。无法找到它的根本原因。以下是我的代码,它获取 API 响应并将其反序列化为一个对象。
public async static Task<T2> ExecuteRequest<T1, T2>(T1 t1, EndPoint endPoint, bool getParams = false, HttpVerb method = HttpVerb.POST)
{
string resStr = "";
try
{
object data = t1;
if (endPoint.ToString() != EndPoints.Login.ToString() && getParams == false)
data = Newtonsoft.Json.JsonConvert.SerializeObject(t1);
HttpUtils.RestClient restClient = new HttpUtils.RestClient(APIPath + endPoint, HttpVerb.POST, data.ToString());
restClient.ContentType = "application/json";
restClient.Method = method;
resStr = await (endPoint != EndPoints.Login && getParams == false ? restClient.MakeRequest() : restClient.MakeRequest(data.ToString()));
if (string.IsNullOrEmpty(resStr))
return default(T2);
return Newtonsoft.Json.JsonConvert.DeserializeObject<T2>(resStr);
}
catch (Newtonsoft.Json.JsonSerializationException e)
{
Device.BeginInvokeOnMainThread(async () => await Application.Current.MainPage.DisplayAlert("Error", e.Message + "\n----------\n" + resStr, "OK"));
return default(T2);
}
catch (System.Net.WebException)
{
Device.BeginInvokeOnMainThread(async () => await Application.Current.MainPage.DisplayAlert("Not Connected", "Please check your Internet connection and restart the app.", "OK"));
return default(T2);
}
}
以下是我尝试使用上述方法登录后端系统的方式:
Services.API.ResponseModels.Login res = await APIAccess.ExecuteRequest<string, Services.API.ResponseModels.Login>(req, APIAccess.EndPoints.Login);
对象Class:
public class Login
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string userName { get; set; }
public string issued { get; set; }
public string expires { get; set; }
}
我正确收到了以下 JSON 格式的回复:
{
"access_token": "<access token received here>",
"token_type": "bearer",
"expires_in": 2591999,
"userName": "<userName received here>",
".issued": "Sun, 16 Jan 2022 09:41:06 GMT",
".expires": "Tue, 15 Feb 2022 09:41:06 GMT"
}
但在调试模式下,NewtonSoft 正确反序列化它并将用户带到另一个屏幕,而在发布模式下,它显示以下错误:
我尝试正常添加默认构造函数并使用 [JsonConstructor]
属性,但它不起作用。我清理并删除了所有项目中的 Bin 和 Obj,还更新了 NewtonSoft JSON 包。在 Release 模式下似乎没有任何效果。
那么为什么你不使用新的 T2() 而不是默认的 (T2) 来尝试呢?
为此,您需要在定义操作时定义 new(),class。
这是一个 linker 问题,您需要提示它不应删除该构造函数,因为它永远不会被直接调用。
一个技巧是创建一个 class,类似 LinkerPleaseInclude
的东西,并创建提示通常会被 link 淘汰的东西的用法的方法。确保告诉编译器不要 link 使用 [Preserve]
属性去掉这个 class。
这可能看起来像:
[Preserve(AllMembers = true)]
public class LinkerPleaseInclude
{
public void Include(Login login)
{
login = new Login();
}
}
然后对所有需要它的类型执行此操作。
您可以将 PreserveAttribute
添加到您的 class 图书馆:
public sealed class PreserveAttribute : Attribute
{
public bool AllMembers;
public bool Conditional;
public PreserveAttribute (bool allMembers, bool conditional)
{
AllMembers = allMembers;
Conditional = conditional;
}
public PreserveAttribute ()
{
}
}
然后在您的 JSON model/class:
上使用该属性[Preserve(AllMembers = true)]
public class Login
{
public string access_token { get; set; }
public string token_type { get; set; }
public int expires_in { get; set; }
public string userName { get; set; }
public string issued { get; set; }
public string expires { get; set; }
}