You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET Core 2.2 WebAPI序列化与反序列化适配问题求助

Solution for JSON Serialization/Deserialization Issues in .NET Core 2.2 WebAPI

Hey there! Let's work through your two serialization/deserialization problems step by step—no extra model conversions required, and we'll make that generic PostAsync method flexible enough to handle both snake_case and camelCase APIs.


Problem 1: Model returns snake_case to mobile when you want camelCase

Your LoginResponseModel uses [JsonProperty(PropertyName = "token_type")] which works great for deserializing third-party snake_case APIs, but this attribute is bidirectional—it tells Newtonsoft.Json to use the snake_case name both when reading and writing JSON. That's why your mobile response ends up in snake_case.

If you can update your Newtonsoft.Json package to version 12.0.1 or higher, replace [JsonProperty] with [JsonAlias]. This attribute only affects deserialization (it tells the serializer to accept the snake_case alias), while serialization will use your property's camelCase name.

Updated LoginResponseModel:

public class LoginResponseModel { 
    [JsonAlias("token_type")] 
    public string tokenType { get; set; } 
    [JsonAlias("access_token")] 
    public string accessToken { get; set; } 
    [JsonAlias("expires_in")] 
    public string expiresIn { get; set; } 
    [JsonAlias("refresh_token")] 
    public string refreshtToken { get; set; } 
}

Then, ensure your WebAPI is configured to output camelCase globally. Add this to Startup.cs in the ConfigureServices method:

services.AddMvc()
    .AddJsonOptions(options =>
    {
        options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    });

Now when you return LoginResponseModel to mobile, it'll be in camelCase, and you can still deserialize third-party snake_case responses without issues.

Fix 2: Custom Contract Resolver (If you can't update Newtonsoft.Json)

If you're stuck on an older Newtonsoft version, create a custom contract resolver that ignores [JsonProperty] during serialization but respects it during deserialization:

public class SerializeCamelCaseDeserializeJsonPropertyResolver : CamelCasePropertyNamesContractResolver
{
    protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
    {
        var property = base.CreateProperty(member, memberSerialization);
        
        // For serialization: use the property's camelCase name
        var originalPropertyName = member.Name.ToCamelCase();
        property.PropertyName = originalPropertyName;
        
        // For deserialization: add the [JsonProperty] value as an alias
        var jsonPropAttr = member.GetCustomAttribute<JsonPropertyAttribute>();
        if (jsonPropAttr != null)
        {
            property.AlternateNames = property.AlternateNames.Concat(new[] { jsonPropAttr.PropertyName }).ToArray();
        }
        
        return property;
    }
}

// Helper extension method for camelCase conversion
public static string ToCamelCase(this string input)
{
    if (string.IsNullOrEmpty(input)) return input;
    return char.ToLowerInvariant(input[0]) + input.Substring(1);
}

Then update your global WebAPI settings to use this resolver instead of the default:

services.AddMvc()
    .AddJsonOptions(options =>
    {
        options.SerializerSettings.ContractResolver = new SerializeCamelCaseDeserializeJsonPropertyResolver();
    });

Problem 2: Generic PostAsync can't handle camelCase APIs

Your current PostAsync hardcodes snake_case deserialization, which breaks when the third-party API returns camelCase. We'll make this method flexible to accept custom deserialization settings.

Fix: Add an Optional Parameter for Deserialization Settings

Modify your PostAsync method to accept an optional JsonSerializerSettings parameter. If no settings are provided, it defaults to snake_case (keeping your existing behavior for snake_case APIs):

private async Task<object> PostAsync<T1,T2>(string uri, T2 content, JsonSerializerSettings deserializationSettings = null) { 
    using (var requestMessage = new HttpRequestMessage(HttpMethod.Post, uri)) { 
        var json = JsonConvert.SerializeObject(content); 
        using (var stringContent = new StringContent(json, Encoding.UTF8, "application/json")) { 
            requestMessage.Content = stringContent; 
            HttpResponseMessage response = await _client.SendAsync(requestMessage); 
            if (response.IsSuccessStatusCode) { 
                _logger.LogInformation("Request Succeeded"); 
                // Use provided settings, or default to snake_case
                var settings = deserializationSettings ?? new JsonSerializerSettings { 
                    ContractResolver = new DefaultContractResolver { 
                        NamingStrategy = new SnakeCaseNamingStrategy() 
                    } 
                };
                T1 responseModel = JsonConvert.DeserializeObject<T1>(await response.Content.ReadAsStringAsync(), settings); 
                return responseModel; 
            } else { 
                return await GetFailureResponseModel(response); 
            } 
        } 
    } 
}

Usage Examples

  • For snake_case APIs (no change needed):
    var loginResponse = await PostAsync<LoginResponseModel, LoginRequestModel>("https://third-party-snake-api.com/login", loginRequest);
    
  • For camelCase APIs:
    var camelCaseSettings = new JsonSerializerSettings { 
        ContractResolver = new CamelCasePropertyNamesContractResolver() 
    };
    var camelCaseResponse = await PostAsync<CamelCaseResponseModel, RequestModel>("https://third-party-camel-api.com/data", request, camelCaseSettings);
    

Bonus: Add Convenience Methods (Optional)

To make calling easier, add wrapper methods for common naming strategies:

public async Task<object> PostSnakeCaseAsync<T1,T2>(string uri, T2 content)
{
    return await PostAsync<T1,T2>(uri, content, new JsonSerializerSettings { 
        ContractResolver = new DefaultContractResolver { 
            NamingStrategy = new SnakeCaseNamingStrategy() 
        } 
    });
}

public async Task<object> PostCamelCaseAsync<T1,T2>(string uri, T2 content)
{
    return await PostAsync<T1,T2>(uri, content, new JsonSerializerSettings { 
        ContractResolver = new CamelCasePropertyNamesContractResolver() 
    });
}

Now you can call:

var loginResponse = await PostSnakeCaseAsync<LoginResponseModel, LoginRequestModel>("https://third-party-snake-api.com/login", loginRequest);
var camelCaseResponse = await PostCamelCaseAsync<CamelCaseResponseModel, RequestModel>("https://third-party-camel-api.com/data", request);

内容的提问来源于stack exchange,提问作者Shabir jan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.12 05:23:15