Я пишу некоторые API-интерфейсы, используя Переустановить, который творит чудеса, и у меня возникают проблемы с поиском хорошего (как в «чистом», «правильном») способе произвольной обработки возвращаемых данных.
В качестве примера рассмотрим этот код:
public interface ISomeService
{
[Get("/someurl/{thing}.json")]
Task<Data> GetThingAsync([AliasAs("thing")] string thing);
}
Многие REST API, которые я видел, имеют неудачную привычку упаковывать фактические данные (например, «полезные» данные) глубоко в ответ JSON. Скажем, фактический JSON имеет такую структуру:
{
"a" = {
"b" = {
"data" = {
...
}
Теперь, как правило, я просто отображал все необходимые модели, что позволило бы Refit правильно десериализовать ответ. Однако это делает API немного неудобным в использовании, поскольку каждый раз, когда я его использую, мне приходится делать что-то вроде:
var response = await SomeService.GetThingAsync("foo");
var data = response.A.B.Data;
Я говорю о том, что эти две внешние модели на самом деле являются просто контейнерами, которые не нужно открывать пользователю. Или, скажем, свойство Data - это модель, у которой есть другое свойство, которым является IEnumerable, я вполне мог бы просто захотеть напрямую вернуть это пользователю.
Я понятия не имею, как это сделать без необходимости писать бесполезные классы-оболочки для каждой службы, где каждый из них также должен, очевидно, повторять все комментарии XML в интерфейсах и т. д., Что приводит к появлению еще более бесполезного кода, плавающего вокруг.
Я просто хотел бы иметь какой-то простой, необязательный эквивалент Func<T, TResult>, который вызывается в результате данного API Refit и вносит некоторые изменения в возвращаемые данные перед их представлением пользователю.





Вы можете передать настраиваемый JsonConverters в Refit, чтобы изменить способ сериализации или десериализации различных типов.
Класс RefitSettings предоставляет параметры настройки, включая параметры сериализатора JSON.
Помните, что класс RefitSettings несколько изменился за последние несколько выпусков. Вам следует проконсультироваться с соответствующей документацией для вашей версии Refit.
Из последней версии Refit Примеры
var myConverters = new List<JsonConverter>();
myConverters += new myCustomADotBConverter();
var myApi = RestService.For<IMyApi>("https://api.example.com",
new RefitSettings {
ContentSerializer = new JsonContentSerializer(
new JsonSerializerSettings {
ContractResolver = new CamelCasePropertyNamesContractResolver(),
Converters = myConverters
}
)});
Вот базовый пример настраиваемого JsonConverter из Документы JSON.Net.
public class VersionConverter : JsonConverter<Version>
{
public override void WriteJson(JsonWriter writer, Version value, JsonSerializer serializer)
{
writer.WriteValue(value.ToString());
}
public override Version ReadJson(JsonReader reader, Type objectType, Version existingValue, bool hasExistingValue, JsonSerializer serializer)
{
string s = (string)reader.Value;
return new Version(s);
}
}
public class NuGetPackage
{
public string PackageId { get; set; }
public Version Version { get; set; }
}
Этот пример JsonConverter предназначен для сериализации или десериализации поля «Версия» полезной нагрузки JSON, которая выглядит следующим образом:
{
"PackageId": "Newtonsoft.Json",
"Version": "10.0.4"
}
Вам нужно будет написать свой собственный JsonConverter для вложенной структуры данных, которую вы хотите десериализовать.
Спасибо за отзыв и публикацию найденного решения! Я поддержал ваш ответ в надежде, что в будущем он появится раньше моего.
Я обнаружил, что достаточно чистым решением этой проблемы является использование методов расширения для расширения услуг Refit. Например, скажем, у меня есть сопоставление JSON, подобное этому:
public class Response
{
[JsonProperty("container")]
public DataContainer Container { get; set; }
}
public class DataContainer
{
[JsonProperty("data")]
public Data Data { get; set; }
}
public class Data
{
[JsonProperty("ids")]
public IList<string> Ids { get; set; }
}
И вместо этого у меня есть API Refit, подобный этому:
public interface ISomeService
{
[Get("/someurl/{thing}.json")]
[EditorBrowsable(EditorBrowsableState.Never)]
[Obsolete("use extension " + nameof(ISomeService) + "." + nameof(SomeServiceExtensions.GetThingAsync))]
Task<Response> _GetThingAsync(string thing);
}
Я могу просто определить такой метод расширения и использовать его вместо API, предоставляемого службой Refit:
#pragma warning disable 612, 618
public static class SomeServiceExtensions
{
public static Task<Data> GetThingAsync(this ISomeService service, string thing)
{
var response = await service._GetThingAsync(thing);
return response.Container.Data.Ids;
}
}
Таким образом, всякий раз, когда я вызываю GetThingAsync API, я фактически использую метод расширения, который может позаботиться обо всей дополнительной десериализации за меня.
Привет, спасибо за ответ, но на самом деле это не ответ на мой вопрос. Как я показал в своем примере, мне не нужно настраивать работу десериализации по умолчанию, но нужно выполнять дополнительную обработку десериализованных данных вместо того, чтобы напрямую возвращать их вызывающей стороне. Или выполнить настраиваемую десериализацию для типов возвращаемых значений, которые являются переменными (например, массив JSON с объектами разных типов). Ни один из этих двух случаев не может быть легко обработан с помощью специального десериализатора.