Контракт ContractResolver у Newtonsoft. JSON конфликтует с пользовательским конвертером - PullRequest
1 голос
/ 04 мая 2020

У меня есть этот пример иерархических данных:

{
  "Name": "Car 1",
  "AvailableColors": [
    "Red",
    "Green"
  ],
  "RelatedItems": {
    "Brand": {
      "Name": "Brand 1",
      "RelatedItems": {
        "ImportingCompanies": [
          {
            "Name": "Company 1",
            "RelatedItems": {
              "CeoName": "CEO 1"
            }
          },
          {
            "Name": "Company 2",
            "RelatedItems": {
              "CeoName": "CEO 2"
            }
          }
        ]
      }
    }
  }
}

По сути, мне нужно скопировать все свойства любого RelatedItems в его родительский объект. Результат должен выглядеть так:

{
  "Name": "Car 1",
  "AvailableColors": [
    "Red",
    "Green"
  ],
  "Brand": {
    "Name": "Brand 1",
    "ImportingCompanies": [
      {
        "Name": "Company 1",
        "CeoName": "CEO 1"
      },
      {
        "Name": "Company 2",
        "CeoName": "CEO 2"
      }
    ]
  }
}

Я использую ASP. NET Ядро, и я настроил свой запуск следующим образом:

.AddNewtonsoftJson(options =>
{
    options.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver();
    options.SerializerSettings.Converters.Add(new RelatedItemsFlattenerJsonConverter());
});

, и это код, который я написано для RelatedItemsFlattenerJsonConverter:

public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    JToken token = JToken.FromObject(value);
    if (token.Type == JTokenType.Array)
    {
        var array = (JArray)token;
        foreach (var item in token)
        {
            FlattenRelatedItems(item);
        }
    }
    else if (token.Type == JTokenType.Object)
    {
        var @object = (JObject)token;
        FlattenRelatedItems(@object);
    }
    token.WriteTo(writer);
    stopwatch.Stop();
}

private void FlattenRelatedItems(JToken token)
{
    if (token.Type == JTokenType.Array)
    {
        var array = (JArray)token;
        foreach (var item in token)
        {
            FlattenRelatedItems(item);
        }
    }
    else if (token.Type == JTokenType.Object)
    {
        var @object = (JObject)token;
        var relatedItemsProperty = @object.Properties().FirstOrDefault(i => i.Name.ToLower() == "RelatedItems".ToLower());
        if (relatedItemsProperty.IsNotNull())
        {
            var relatedItems = @object[relatedItemsProperty.Name];
            @object.Remove(relatedItemsProperty.Name);
            var keys = ((JObject)relatedItems).Properties().Select(i => i.Name).ToList();
            foreach (var key in keys)
            {
                @object.Add(key, relatedItems[key]);
            }
        }
        var properties = @object.Properties().ToList();
        foreach (var property in properties)
        {
            FlattenRelatedItems(@object[property.Name]);
        }
    }
}

Работает как шарм. Однако, когда я добавлю пользовательский RelatedItemsFlattenerJsonConverter, регистр моего ответа API станет PascalCase d. И когда я не добавляю его, ContractResolver соблюдается, и мой ответ API - camelCase d. Что я должен сделать, чтобы иметь и пользовательский конвертер, и camelCase для API?

1 Ответ

0 голосов
/ 04 мая 2020

Когда вы вызываете JToken.FromObject(value) внутри WriteJson, вы не передаете ему сериализатор, поэтому он не знает о CamelCasePropertyNamesContractResolver (или любых других настройках), которые вы настроили. JToken.FromObject() по умолчанию использует новый экземпляр сериализатора.

Однако в этом случае, если вы передадите сериализатор как есть, вы можете столкнуться с самоссылающейся l oop, когда конвертер пытается называть себя. Что вам нужно сделать, это создать свой собственный экземпляр JsonSerializer внутри WriteJson, а затем скопировать ссылку на преобразователь (и любые другие важные настройки, которые вы хотите сохранить, конечно, за исключением конвертера) из сериализатора, который был передан WriteJson. Затем передайте этот новый сериализатор на JToken.FromObject().

Другими словами, измените эту строку:

JToken token = JToken.FromObject(value);

На это:

var innerSerializer = new JsonSerializer();
innerSerializer.ContractResolver = serializer.ContractResolver;
// if any other settings from the outer serializer are needed, copy them here
JToken token = JToken.FromObject(value, innerSerializer);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...