Разбор объекта со списком свойств <Claim>и указание JsonConvert.DeserializeObject использовать конкретный конструктор для утверждений? - PullRequest
0 голосов
/ 06 июня 2019

Я использую .NET Core с Newtonsoft.Json. У меня есть UserModel класс, который имеет List<Claim> свойство

public class UserModel
{        
    public string GUID { get; set; }
    public bool isActive { get; set; }
    public string Username { get; set; }
    public string Password { get; set; }
    public List<Claim> Claims { get; set; }
}

и я пытаюсь проанализировать JSON-запрос в этот класс объектов следующим образом:

public IActionResult Testpost([FromBody]JObject body)
{
    if (body == null) return BadRequest();

    UserModel user = JsonConvert.DeserializeObject<UserModel>(body.ToString());

    return Ok(user);
}

но десериализация JSON в объект типа Claim класса, к которому у меня нет доступа, выдает исключение

Newtonsoft.Json.JsonSerializationException: 'Невозможно найти конструктор для использования для типа System.Security.Claims.Claim. Класс должен иметь конструктор по умолчанию, один конструктор с аргументами или конструктор, помеченный атрибутом JsonConstructor. Путь 'Претензии

потому что он не может выбрать конструктор

Согласно источникам в Интернете, я могу создать собственный класс конвертера, который может управлять созданием объекта UserModel, но я бы хотел этого избежать.

Можно ли десериализовать объект JSON в мой класс UserModel и указать JsonConvert.DeserializeObject использовать конкретный конструктор утверждений, например Claim(String, String), для анализа утверждений?

EDIT: как уже упоминалось @PaulG, я уже проверил ответ для Как программно выбрать конструктор во время десериализации?

однако принятое принятое решение Создает новый класс, который реализует класс JsonConverter, а затем вручную анализирует тело запроса JObject. Более того, ответ показывает, как обращаться с утверждениями, но не со сложными объектами, где утверждения вложены в свойства

чтение другого решения в потоке показывает, как создать класс, который непосредственно реализует конструктор, необходимый следующим образом:

class MyClaim : Claim {
public MyClaim(string type, string value):
    base(type, value){}
}

но это потребует от меня помнить разницу между Claim и MyClaim при написании моего кода. JSON-конвертер не может предположить, какой конструктор использовать, но я должен сказать, какой именно. или это умышленно, и я должен смириться с этим и написать дополнительный код только для этого?

потому что альтернативой для меня было бы что-то вроде этого:

    public IActionResult CreatePublicUser([FromBody]JObject body)
    {

        string Username = body["Username"].ToString();
        string Password = body["Password"].ToString();

        var Claims = body["Claims"].Children();

        List<Claim> UserClaims = new List<Claim>();

        foreach (var c in Claims)
        {

            UserClaims.Add(
                new Claim(
                        c["Type"].ToString(), 
                        c["Value"].ToString()
                        )
                    );
        }

 UserModel NewUser = (new UserBuilder())
            .WithUserName(Username)
            .WithPassword(Password)
            .WithClaims(UserClaims)
            .Build();

 return Ok(NewUser)
}

1 Ответ

0 голосов
/ 06 июня 2019

Я предлагаю вам использовать другой подход, который также является обычной практикой. Определите модель только для взаимодействия с клиентом, например

public class UserModelWeb
{        
    public List<ClaimWeb> Claims { get; set; }
}

Эти объекты DTO будут использоваться только для преобразования данных из JSON и никогда на уровне бизнес-логики. Затем вы можете сопоставить свои веб-модели с бизнес-логикой, которую вы будете использовать позже. Это позволит вам не зависеть от внутренних классов при чтении внешних данных. То есть если вы неожиданно добавите новое поле, оно будет заполнено из внешнего (и, вероятно, ненадежного) источника. Такое четкое разделение интересов не позволит этого, поскольку вам придется явно определить поле в веб-модели.

Пример для вашего случая: допустим, позже у вас будет внутреннее поле в базе данных, которое может редактировать только вы: «InternalNote». Если вы добавите это поле в модель, любой сможет опубликовать данные и отредактировать это поле, пока вы только захотите отредактировать его.

Кроме того, это решит вашу проблему, поскольку вам не нужно приводить к другим классам.

P.S. Вы можете использовать свой класс непосредственно в методах действия:

MyAction([FromBody]UserModelWeb user)

Он должен быть немедленно десериализован из JSON.

...