> Как обрабатывать отсутствие одинаковых строк в JSON (C#)

Уровень: senior · Роль: backend · Язык: C# · Категория: Технические вопросы

Компании: Ozon

Стек: C# /.NET

> Пример ответа

Короткий ответ

В C# для JSON отсутствие одинаковых строк обычно означает, что нужно обработать ситуацию, когда в массиве или объекте нет дубликатов. Это не ошибка парсинга, а бизнес-случай. Следует явно проверять наличие повторов через группировку или Distinct, а при необходимости возвращать пустой результат, null или бросать исключение. Ключевой момент - не полагаться на неявное поведение System.Text.Json или Newtonsoft.Json, а задавать политику на уровне домена.

Подробное объяснение

Под "отсутствием одинаковых строк" в JSON можно понимать две разные задачи. Первая - в JSON-массиве строк нет двух одинаковых значений, и нужно корректно обработать этот факт. Вторая - при десериализации в словарь или объект с ключами-строками отсутствует ожидаемый дубликат, хотя по логике он должен быть. На уровне senior-разработчика важно разделять эти сценарии и не смешивать валидацию данных с обработкой исключений.

Если речь о массиве строк, то после десериализации в List<string> или string[] можно проверить уникальность через GroupBy или Distinct. При отсутствии дубликатов поведение зависит от контракта: либо это нормально и возвращается исходная коллекция, либо это нарушение инварианта и нужно выбросить InvalidOperationException или вернуть Result-подобный объект. В .NET принято избегать исключений для ожидаемых бизнес-ситуаций, поэтому лучше использовать явные проверки и Try-паттерны.

Если речь о JSON-объекте, где ключи должны быть уникальными, то стандарт JSON не запрещает дубликаты, но большинство парсеров берут последнее значение. В System.Text.Json при десериализации в Dictionary<string, T> дубликаты ключей приводят к исключению JsonException только если включена опция AllowDuplicateProperties = false (по умолчанию в .NET 8+ дубликаты перезаписываются). Для Newtonsoft.Json поведение аналогично: последний ключ побеждает. Поэтому "отсутствие одинаковых строк" может означать, что дубликатов просто нет, и это не требует специальной обработки, кроме проверки ожидаемого количества.

На уровне архитектуры важно определить, является ли уникальность строки частью контракта. Если да, то проверка должна быть явной и документированной. Если нет, то не нужно добавлять лишние проверки, которые усложняют код и снижают производительность. Также стоит учитывать регистрозависимость: в C# string по умолчанию сравнивается с учётом регистра, но для JSON-ключей часто используют StringComparer.OrdinalIgnoreCase. Это влияет на то, считаются ли строки одинаковыми.

На практике

На практике в backend-сервисе на C# чаще всего приходит JSON-массив строк, и нужно понять, есть ли в нём дубликаты. Для этого после десериализации применяют Distinct() или GroupBy(). Если дубликатов нет, а по бизнес-логике они ожидались, возвращают 400 Bad Request с понятным сообщением. Если дубликаты не ожидались, но появились, это может быть признаком ошибки на стороне клиента или атаки, поэтому логируют и отклоняют запрос.

Для JSON-объектов с ключами-строками важно явно указать политику дубликатов при настройке сериализатора. В System.Text.Json можно использовать JsonSerializerOptions с AllowDuplicateProperties (в .NET 8+) или вручную проверять через JsonDocument. В Newtonsoft.Json есть DuplicatePropertyHandling. Это позволяет избежать неявного перезаписывания и сделать поведение предсказуемым.

Также стоит помнить о производительности: GroupBy и Distinct создают дополнительные аллокации. Для больших массивов лучше использовать HashSet<string> с нужным компаратором. Это особенно важно в highload-сценариях, где каждый лишний проход по коллекции может быть заметен.

Пример кода

CSHARP
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
public class JsonStringUniqueness
{
public static bool HasDuplicates(string jsonArray)
{
var strings = JsonSerializer.Deserialize<List<string>>(jsonArray);
if (strings == null) return false;
var set = new HashSet<string>(StringComparer.Ordinal);
foreach (var s in strings)
{
if (!set.Add(s))
return true;
}
return false;
}
public static List<string> GetUniqueOrThrow(string jsonArray)
{
var strings = JsonSerializer.Deserialize<List<string>>(jsonArray);
if (strings == null)
throw new ArgumentException("JSON array is null");
var set = new HashSet<string>(StringComparer.Ordinal);
var result = new List<string>();
foreach (var s in strings)
{
if (set.Add(s))
result.Add(s);
}
if (result.Count != strings.Count)
throw new InvalidOperationException("Duplicate strings are not allowed");
return result;
}
}

Как отвечать на собеседовании

Начните с уточнения, что именно имеется в виду: отсутствие дубликатов в массиве строк или отсутствие ожидаемого дубликата ключа в JSON-объекте. Затем объясните, что в C# для массивов строк используется HashSet или Distinct с явным компаратором. Для JSON-объектов важно указать, что стандарт JSON не запрещает дубликаты ключей, но парсеры ведут себя по-разному, и это нужно контролировать через настройки. Подчеркните, что обработка отсутствия дубликатов - это бизнес-решение, а не техническая ошибка, поэтому лучше возвращать результат или использовать Try-паттерн, а не бросать исключения без необходимости. Упомяните про регистрозависимость и производительность.

Что проверяет интервьюер

Интервьюер хочет увидеть, что кандидат различает уровни: парсинг JSON, валидация данных и бизнес-логика. Проверяется понимание поведения System.Text.Json и Newtonsoft.Json при дубликатах ключей, умение работать с HashSet и компараторами, а также навык выбирать между исключениями и возвратом результата. Также важно, чтобы кандидат учитывал регистрозависимость и производительность на больших данных. Ожидается, что senior не будет путать отсутствие дубликатов с ошибкой десериализации и сможет предложить чистое, поддерживаемое решение.

Типичные ошибки

Частая ошибка - использовать Distinct() без указания компаратора, что приводит к регистрозависимому сравнению, тогда как JSON-ключи часто должны сравниваться без учёта регистра. Другая ошибка - полагаться на то, что парсер сам выбросит исключение при дубликатах ключей, хотя по умолчанию многие парсеры просто перезаписывают значение. Также часто путают отсутствие дубликатов с отсутствием данных и возвращают null вместо пустой коллекции. Ещё одна ошибка - бросать исключение для ожидаемой бизнес-ситуации, что усложняет обработку и снижает производительность. Наконец, забывают про многопоточность и конкурентный доступ к HashSet, если проверка выполняется в параллельном коде.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью