Automatically bind pascal case c# model from snake case JSON in WebApi

前端 未结 3 960
一向
一向 2021-01-11 12:53

I am trying to bind my PascalCased c# model from snake_cased JSON in WebApi v2 (full framework, not dot net core).

Here\'s my api:

public class MyAp         


        
3条回答
  •  一向
    一向 (楼主)
    2021-01-11 13:46

    Well, you should be able to do it using a custom JsonConverter to read your data. Using the deserialization provided in Manojs' answer, you could create a DefaultContractResolver that would create a custom deserialization when the class has a SnakeCasedAttribute specified above.

    The ContractResolver would look like the following

    public class SnakeCaseContractResolver : DefaultContractResolver {
      public new static readonly SnakeCaseContractResolver Instance = new SnakeCaseContractResolver();
    
      protected override JsonContract CreateContract(Type objectType) {
        JsonContract contract = base.CreateContract(objectType);
    
        if (objectType?.GetCustomAttributes(true).OfType().Any() == true) {
          contract.Converter = new SnakeCaseConverter();
        }
    
        return contract;
      }
    }
    

    The SnakeCaseConverter would be something like this?

    public class SnakeCaseConverter : JsonConverter {
      public override bool CanConvert(Type objectType) => objectType.GetCustomAttributes(true).OfType().Any() == true;
      private static string ConvertFromSnakeCase(string snakeCased) {
        return string.Join("", snakeCased.Split('_').Select(part => part.Substring(0, 1).ToUpper() + part.Substring(1)));
      }
    
      public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        var target = Activator.CreateInstance( objectType );
        var jobject = JObject.Load(reader);
    
        foreach (var property in jobject.Properties()) {
          var propName = ConvertFromSnakeCase(property.Name);
          var prop = objectType.GetProperty(propName);
          if (prop == null || !prop.CanWrite) {
            continue;
          }
          prop.SetValue(target, property.Value.ToObject(prop.PropertyType, serializer));
        }
        return target;
      }
    
      public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        throw new NotImplementedException();
      }
    }
    

    And then you could annotate your dto class using this attribute (which is just a placeholder)

    [SnakeCased]
    public class InputObjectDTO {
      public string FullName { get; set; }
      public int TotalPrice { get; set; }
    }
    

    and for reference, this is the used attribute

    [AttributeUsage(AttributeTargets.Class)]
    public class SnakeCasedAttribute : Attribute {
      public SnakeCasedAttribute() {
        // intended blank
      }
    }
    

    One more thing to notice is that in your current form the JSON converter would throw an error ("20.00" is not an int), but I am going to guess that from here you can handle that part yourself :)

    And for a complete reference, you could see the working version in this dotnetfiddle

提交回复
热议问题