Respan Dataset Explorer

Select one behavior. Every returned turn has one binary label: Present or Absent. Source: final dense boolean release.

5,167,182physical rows
86shards
0.00%qualified row coverage
0.00%qualified cell coverage
Random row JSON API

turns-00084.parquet:40464

b2916d6a2300db62479ae0f3
turn 4/19gpt-4.1-mini-2025-04-14RussianRussia676 words
degenerate_repetitionAbsentFinal dense release
USER
package ru.mge.estimate.model

import java.math.BigDecimal
import java.math.BigInteger

sealed class ValueType {
    abstract val type: Type
    abstract var validationResult: ValidationResult?

    data class DecimalValue(
        val value: BigDecimal? = null,
        override val type: Type = Type.NUMBER,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

    data class IntegerValue(
        val value: BigInteger? = null,
        override val type: Type = Type.NUMBER,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

    data class StringValue(
        val value: String? = null,
        override val type: Type = Type.STRING,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

...
}
ASSISTANT
Спасибо за дополнение с кодом `ValueType` — теперь всё понятно.

---

## Корневая причина проблемы

У вас есть sealed class `ValueType`, у которого есть несколько *data class* наследников (`DecimalValue`, `IntegerValue`, `StringValue`), и у каждого из них поле `value`.

springdoc-openapi (через Jackson) при генерации схемы распознаёт не простой тип `string` или `number`, а **объект с полями**:

- `value` (типа string, number…)
- `type` (enum или string)
- `validationResult` (какой-то объект)

То есть для Swagger UI получается довольно сложная модель `ValueType.StringValue`, которая никак не сводится к простому примитиву — потому в UI вы видите вложенный объект с полями `value`, `type`, `validationResult`.

Кроме того, в вашем примере в UI появляются поля, которых нет в исходном классе, как `objectNum`, `objectQuantityUnit` и прочие — возможно, они принадлежат не к `MetaDto`, а к другим схожим классам, или в UI подтягиваются из других источников.

---

## Как сделать так, чтобы в Swagger UI поля **представлялись именно как примитивы?**

### 1. Используйте `@JsonValue` в классах-обёртках

Jackson при сериализации, если в классе есть метод, помеченный `@JsonValue`, сериализует не весь объект, а именно результат этого метода.

В Kotlin для `data class` это будет поле `value`. Вам нужно пометить поле `value` в каждом наследнике аннотацией `@get:JsonValue`, чтобы Jackson при сериализации понимал, что значение этого класса — просто `value`.

Пример:

```kotlin
import com.fasterxml.jackson.annotation.JsonValue
import io.swagger.v3.oas.annotations.media.Schema

sealed class ValueType {
    abstract val type: Type
    abstract var validationResult: ValidationResult?

    data class DecimalValue(
        @get:JsonValue
        @Schema(description = "Decimal value example", example = "123.45")
        val value: BigDecimal? = null,
        override val type: Type = Type.NUMBER,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

    data class IntegerValue(
        @get:JsonValue
        @Schema(description = "Integer value example", example = "123")
        val value: BigInteger? = null,
        override val type: Type = Type.NUMBER,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

    data class StringValue(
        @get:JsonValue
        @Schema(description = "String value example", example = "example text")
        val value: String? = null,
        override val type: Type = Type.STRING,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()
}
```

### Что это даёт?

- Jackson будет сериализовать `StringValue("hello")` как просто `"hello"` (а не как объект с тремя полями)
- SpringDoc воспримет этот класс как тип **примитива** (string, number) и подтянет схему корректно
- В OpenAPI схема для этих классов станет простой примитивной, без вложенных полей, что сделает UI понятнее

---

### 2. Добавьте аннотацию `@Schema` на класс или поле

Если хотите быть точнее в спецификации схемы, добавьте аннотации `@Schema`:

```kotlin
@Schema(type = "string", description = "Обёртка над строковым значением")
data class StringValue( ... )
```

---

### 3. (Опционально) Исключите лишние поля из схемы

Если `type` и `validationResult` не нужны в API (либо не должны показываться в UI), можно пометить их:

```kotlin
@Schema(hidden = true)
override val type: Type = Type.STRING

@Schema(hidden = true)
override var validationResult: ValidationResult? = null
```

Чтобы они не попадали в описание схемы.

---

### 4. Пересоберите проект и проверьте `/v3/api-docs`

После внесения изменений зайдите в JSON сгенерированной OpenAPI спецификации, убедитесь, что:

- `StringValue` теперь подается просто как `type: string`  
- Для `MetaDto.softName` и остальных полей тип — string, integer или number  
- В Swagger UI поля отображаются как примитивы, а не вложенные объекты

---

## Итог

- Проблема в том, что `ValueType.XValue` — это сложные объекты, а не примитивы, для OpenAPI они становятся объектами с вложенными свойствами.  
- Аннотация `@JsonValue` на геттер `value` решает проблему, заставляя Jackson и springdoc воспринимать весь объект как примитив с этим значением.  
- Дополнительно `@Schema` помогает подсказать OpenAPI тип (string/number).

Это самое корректное и классическое решение для подобной задачи.

---

Если будет нужно — могу помочь с примером полного минимального кода.

turns-00084.parquet:40465

2aab605ad2ed6e328951edc5
turn 5/19gpt-4.1-mini-2025-04-14RussianRussia347 words
degenerate_repetitionAbsentFinal dense release
USER
import com.fasterxml.jackson.annotation.JsonValue
import io.swagger.v3.oas.annotations.media.Schema

sealed class ValueType {
    abstract val type: Type
    abstract var validationResult: ValidationResult?

    data class DecimalValue(
        @get:JsonValue
        @Schema(description = "Decimal value example", example = "123.45")
        val value: BigDecimal? = null,
        override val type: Type = Type.NUMBER,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

    data class IntegerValue(
        @get:JsonValue
        @Schema(description = "Integer value example", example = "123")
        val value: BigInteger? = null,
        override val type: Type = Type.NUMBER,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

    data class StringValue(
        @get:JsonValue
        @Schema(description = "String value example", example = "example text")
        val value: String? = null,
        override val type: Type = Type.STRING,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()
}

Эти добавления повлияют на получаемый json из соотвесвующего эндпоинта?
ASSISTANT
Да, аннотация `@JsonValue` влияет на сериализацию в JSON: при её наличии Jackson будет сериализовать объект класса (например, `StringValue`) **не как объект с полями**, а как значение, возвращаемое методом (у вас — это поле `value`).

---

### Что изменится в JSON выводу?

Пример без `@JsonValue`:

```kotlin
val stringValue = StringValue(value = "hello")
```

Сериализация будет примерно такой:

```json
{
  "value": "hello",
  "type": "STRING",
  "validationResult": null
}
```

---

Пример при наличии `@JsonValue`:

```json
"hello"
```

То есть:

- Вместо объекта с тремя полями, в JSON передаётся **только значение поля `value`**.

---

### Важно:

- Это влияет на все JSON представления, в том числе когда объект возвращается из контроллера и в теле ответа.
- Если в вашем API клиенты ожидают именно объект с полями `value`, `type` и `validationResult`, то такое изменение может нарушить совместимость.
- Если же API логично и ожидаемо представляет `ValueType.StringValue` как просто строку (например, `"example text"`), то это улучшение.

---

### Итог:

- Да, поле, помеченное `@JsonValue`, влияет на JSON выводу и меняет его форму на **примитив** (в вашем случае, строку или число).  
- Это стандартный и рекомендуемый подход, если внутри класса-обёртки важно передавать одно «главное» значение и при этом не нагружать клиента лишними полями.

---

Если вам нужна обратная совместимость или более сложная логика сериализации, можно рассмотреть дополнительные кастомные сериализаторы/десериализаторы Jackson — но для большинства случаев `@JsonValue` решает задачу просто и эффективно.

turns-00084.parquet:40466

d4c4ea3756bd1e325be3a0c9
turn 6/19gpt-4.1-mini-2025-04-14RussianRussia605 words
degenerate_repetitionAbsentFinal dense release
USER
Тогда это все не подходит Нужна отобразить в сваггере все поля как есть в дто учитывая всю вложенность
ASSISTANT
Понял вашу задачу — нужно, чтобы именно **полный объект `ValueType.StringValue` с полями `value`, `type`, `validationResult`** корректно отображался в Swagger UI, и чтобы структура `MetaDto` с вложенными моделями показывалась **полностью, со всеми полями и вложенностями**, без «сплющивания» в примитивы.

---

## Почему такое не отображается сейчас?

По умолчанию springdoc-openapi и Jackson, работая с Kotlin и sealed class, могут представить такую структуру не совсем полно, потому что:

- Jackson по умолчанию не всегда корректно обрабатывает sealed class с наследниками, т.к. не знает, какую именно конкрентную реализацию использовать (нужна полиморфная десериализация/сериализация)
- Springdoc может не раскрывать все свойства sealed class, либо сворачивать модели под одну, для сокращения и оптимизации схемы.

---

## Что нужно сделать, чтобы все поля и вложенности отображались в UI без пропусков?

### 1. Обеспечить правильную полиморфную сериализацию/десериализацию Jackson

Для sealed class (`ValueType`) нужно настроить Jackson на работу с типом (discriminator), чтобы он явно видел, что значения могут быть разными подтипами с разной структурой.

Для этого обычно используют аннотации:

```kotlin
import com.fasterxml.jackson.annotation.JsonSubTypes
import com.fasterxml.jackson.annotation.JsonTypeInfo

@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "typeName"  // имя поля-дискриминатора
)
@JsonSubTypes(
    JsonSubTypes.Type(value = ValueType.StringValue::class, name = "StringValue"),
    JsonSubTypes.Type(value = ValueType.IntegerValue::class, name = "IntegerValue"),
    JsonSubTypes.Type(value = ValueType.DecimalValue::class, name = "DecimalValue")
)
sealed class ValueType {
    ...
}
```

Это заставит Jackson и, соответственно, springdoc видеть, что у `ValueType` есть конкретные подтипы с собственными полями.

---

### 2. Не использовать `@JsonValue` (если нужно показать все поля)

Аннотация `@JsonValue` как раз скрывает всю оболочку и оставляет только одно поле (`value`), поэтому её надо убрать, если нужно показывать полную структуру.

---

### 3. Убедиться, что все поля у наследников openAPI и Jackson видны

Если у вас есть поля `validationResult`, `type`, убедитесь, что они в моделях **публичные** и не аннотированы `@JsonIgnore` или `@Schema(hidden = true)`.

---

### 4. Добавьте аннотации `@Schema` с примерами и описаниями на поля и классы

Чтобы UI показывал более читаемую схему, можете аннотировать модели например так:

```kotlin
import io.swagger.v3.oas.annotations.media.Schema

@Schema(description = "Базовый тип значения")
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "typeName"
)
@JsonSubTypes(
    JsonSubTypes.Type(value = ValueType.StringValue::class, name = "StringValue"),
    JsonSubTypes.Type(value = ValueType.IntegerValue::class, name = "IntegerValue"),
    JsonSubTypes.Type(value = ValueType.DecimalValue::class, name = "DecimalValue")
)
sealed class ValueType {
    @Schema(description = "Тип значения")
    abstract val type: Type
    
    @Schema(description = "Результат валидации")
    abstract var validationResult: ValidationResult?

    @Schema(description = "Строковое значение")
    data class StringValue(
        @field:Schema(description = "Значение строки", example = "example text")
        val value: String? = null,
        override val type: Type = Type.STRING,
        override var validationResult: ValidationResult? = null,
    ) : ValueType()

    // Аналогично для IntegerValue, DecimalValue
}
```

---

### 5. (Опционально) Проверьте настройку springdoc, чтобы он поддерживал Kotlin и sealed классы

- Убедитесь, что в POM/Gradle подключён `kotlin-reflect`  
- Используется ли версия springdoc-openapi, поддерживающая полиморфизм и Kotlin-классы (лучше 1.6.x+ или 2.x последних версий)  
- Проверьте, что никаких кастомных конфигураций Jackson с `ObjectMapper` не убирают необходимые модули

---

### 6. Проверка в UI и JSON спецификации

После этого зайдите в `/v3/api-docs`, проверьте, что для `ValueType` определена **схема с discriminator и всеми подтипами**.

В Swagger UI раскройте `MetaDto` — вы должны увидеть в свойствах поле `softName` с типом `StringValue` и его полями `value`, `type`, `validationResult`.

---

## Итог

- **Уберите аннотации `@JsonValue`**, чтобы видеть весь объект, а не только поле `value`.  
- Добавьте полиморфные аннотации `@JsonTypeInfo` и `@JsonSubTypes`, чтобы Jackson и springdoc видели наследников sealed класса.  
- Аннотируйте классы и поля `@Schema` для удобства и ясности.  
- Проверьте, что kotlin-reflect и необходимые зависимости присутствуют.  
- Проверьте в `/v3/api-docs` схему и UI — все вложенные поля должны присутствовать.

---

Если надо, могу помочь собрать небольшой минимальный пример с таким же паттерном — напишите!

turns-00084.parquet:40467

7c754bef6c72f433f4249797
turn 7/19gpt-4.1-mini-2025-04-14RussianRussia456 words
degenerate_repetitionAbsentFinal dense release
USER
Ничего из этого не помогло
ASSISTANT
Понял, что стандартные методы не сработали — тогда предлагаю более системный подход для корректной генерации полной схемы с вложенностями и sealed классов в springdoc + Kotlin.

---

# 1. Подключите kotlin-reflect

Обязательно в build.gradle или pom.xml добавьте зависимость:

```gradle
implementation "org.jetbrains.kotlin:kotlin-reflect"
```

Без неё генерация по Kotlin-классам будет неполной.

# 2. Убедитесь, что Jackson настроен на поддержку полей Kotlin и sealed классов

Проверьте, что в вашем проекте активирован `jackson-module-kotlin`, который добавляет поддержку Kotlin-специфик.

```gradle
implementation "com.fasterxml.jackson.module:jackson-module-kotlin"
```

И убедитесь, что в конфиге Jackson регистрируется этот модуль:

```kotlin
@Bean
fun objectMapper(): ObjectMapper = jacksonObjectMapper()
    .registerKotlinModule()
    .registerModule(JavaTimeModule()) // если используете временные типы
```

spring-boot-starter-json обычно это делает автоматически, но проверьте.

# 3. Создайте кастомную конфигурацию для полиморфизма Jackson, если необходимо

Для sealed class `ValueType` явные JsonTypeInfo/JsonSubTypes обязательны:

```kotlin
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "typeName",
    visible = true
)
@JsonSubTypes(
    JsonSubTypes.Type(ValueType.StringValue::class, "StringValue"),
    JsonSubTypes.Type(ValueType.IntegerValue::class, "IntegerValue"),
    JsonSubTypes.Type(ValueType.DecimalValue::class, "DecimalValue")
)
sealed class ValueType {
    abstract val type: Type
    abstract var validationResult: ValidationResult?
    // ...
}
```

Параметр `visible = true` помогает Jackson видеть дискриминатор при десериализации и генерации схемы.

# 4. Добавьте в application.properties

Включите полный режим генерации схемы и логирование springdoc:

```properties
springdoc.model-and-view-allowed=true
logging.level.org.springdoc=DEBUG
```

# 5. Проверьте /v3/api-docs

После запуска, скачайте JSON схему по адресу `/v3/api-docs`, найдите описание `MetaDto` и `ValueType`.

Если `ValueType` там не раскрывается с подтипами — значит проблема с генерацией модели Jackson.

# 6. Пример минимального DTO c embedded polymorphic моделью

```kotlin
@JsonTypeInfo(
    use = JsonTypeInfo.Id.NAME,
    include = JsonTypeInfo.As.PROPERTY,
    property = "typeName",
    visible = true
)
@JsonSubTypes(
    JsonSubTypes.Type(ValueType.StringValue::class, "StringValue"),
    JsonSubTypes.Type(ValueType.IntegerValue::class, "IntegerValue"),
)
sealed class ValueType {
    abstract val type: String
    abstract var validationResult: String?

    data class StringValue(
        val value: String?,
        override val type: String = "StringValue",
        override var validationResult: String? = null
    ) : ValueType()

    data class IntegerValue(
        val value: Int?,
        override val type: String = "IntegerValue",
        override var validationResult: String? = null
    ) : ValueType()
}

data class MetaDto(
    val softName: ValueType.StringValue,
    val softVersion: ValueType.StringValue,
)
```

Этот минимальный пример нормально работает в springdoc и полно отображает поля.

# 7. Если это не помогает — попробуйте отдельный тестовый проект

Иногда корень проблемы — в конфликте зависимостей или кастомных конфигурациях. Попробуйте создать минимальный Spring Boot + Kotlin проект с springdoc, добавить этот код и посмотреть результат.

Если там всё правильно — проблема в вашем проекте.

# 8. Крайний вариант — кастомные схемы (openapi.yml) или @Schema(ref = ...)

Если автоматическая генерация не подходит, можно вручную описывать в openapi yaml/json нужные сложные типы, и в DTO ставить

```kotlin
@Schema(ref = "#/components/schemas/YourModel")
class YourModel
```

или генерировать документацию внешним инструментом.

---

Если ничего выше не помогает — пришлите:

- Минимальный воспроизводимый проект/пример  
- Логи springdoc при старте (с DEBUG)  
- JSON из `/v3/api-docs` для вашей модели

Я помогу найти узкое место.

turns-00084.parquet:40468

9f0ca87be3e5a88868b3c418
turn 8/19gpt-4.1-mini-2025-04-14RussianRussia242 words
degenerate_repetitionAbsentFinal dense release
USER
что за настройка springdoc.model-and-view-allowed=true
ASSISTANT
Параметр `springdoc.model-and-view-allowed` — это опция конфигурации springdoc-openapi (начиная примерно с версии 1.6.x), которая относится к определению, разрешено ли учитывать возвращаемые типы контроллеров со значением `ModelAndView` или подобных.

---

### Что делает `springdoc.model-and-view-allowed=true`?

- По умолчанию Spring MVC позволяет контроллерам возвращать объекты типа `ModelAndView` (класс, который содержит модель и название представления), что предполагает рендеринг HTML/страниц. Такие методы обычно не относят к API-эндпоинтам, которые возвращают JSON.

- Springdoc по умолчанию **не учитывает** методы контроллеров, возвращающие `ModelAndView` для генерации OpenAPI документации, чтобы не смешивать дескриптор REST API и рендеринг представлений.

- Если задать в `application.properties`:

  ```properties
  springdoc.model-and-view-allowed=true
  ```

  — то springdoc будет **разрешать и учитывать** методы, возвращающие `ModelAndView`, и генерировать для них OpenAPI описание.

---

### Когда это нужно?

- Например, если у вас контроллеры смешанного типа — где вы и REST API отдаёте, и страницы (views), и хотите, чтобы все были описаны в OpenAPI.

- В вашем случае, когда проблема с отображением моделей, эта настройка практически не влияет и в большинстве случаев не нужна.

---

### В официальной документации springdoc эта опция не является обязательной для корректной генерации моделей и схем.

---

### Резюме

- `springdoc.model-and-view-allowed` — разрешает описывать в OpenAPI методы контроллеров, которые возвращают `ModelAndView` (данные+шаблон).  
- По умолчанию — false, потому что Swagger-UI обычно предназначен для API JSON.  
- В типичных REST сервисах не используется.

---

Если не работает ваша схема — эта опция не поможет и причина в другом (скорее в конфигурации Jackson/Kotlin/sealed class, как обсуждали выше).

turns-00084.parquet:40469

810d1abcff8042027993035b
turn 9/19gpt-4.1-mini-2025-04-14RussianRussia526 words
degenerate_repetitionAbsentFinal dense release
USER
В чем роль конвертера
package ru.mge.estimate.model.local

import com.fasterxml.jackson.databind.ObjectMapper
import jakarta.persistence.AttributeConverter
import jakarta.persistence.Converter
import org.springframework.stereotype.Component

@Converter(autoApply = true)
@Component
class LocalConstructionContextConverter(
    private val objectMapper: ObjectMapper
) : AttributeConverter<LocalConstructionContext, String> {
    override fun convertToDatabaseColumn(attribute: LocalConstructionContext?): String? {
        return attribute?.let { objectMapper.writeValueAsString(it) }
    }

    override fun convertToEntityAttribute(dbData: String?): LocalConstructionContext? {
        return dbData?.let { objectMapper.readValue(it, LocalConstructionContext::class.java) }
    }
}

Может надо создать такой же для  LocalConstructionContextDto в котором находятся все остальные дто
ASSISTANT
Ваш класс `LocalConstructionContextConverter` — это JPA конвертер, который отвечает за преобразование объекта `LocalConstructionContext` в строку (обычно JSON) для сохранения в базу, и обратно — преобразование JSON-строки из БД в объект `LocalConstructionContext`.  

---

## Роль этого конвертера

- **`convertToDatabaseColumn`** — преобразует объект локального контекста в JSON-строку, которую JPA сохранит в какое-то текстовое поле базы (например, `TEXT` или `VARCHAR`).
- **`convertToEntityAttribute`** — наоборот, из JSON-строки из базы создаёт объект `LocalConstructionContext` при загрузке из БД.

Этот механизм позволяет хранить в базе сложные вложенные структуры как сериализованный JSON, сохраняя при этом Java/Kotlin модель в приложении.

---

## Относительно DTO: нужен ли такой конвертер для `LocalConstructionContextDto`?

Отвечая на вопрос: **«Можно ли / нужно ли создавать такой же конвертер для DTO?»** — обычно:

- DTO (Data Transfer Object) используются на уровне передачи данных (например, через API), а не для хранения в базе.
- Конвертеры JPA (`AttributeConverter`) работают с Entity-классами, которые связаны непосредственно с таблицами или колонками в БД.
- Если `LocalConstructionContextDto` — это просто DTO для передачи между слоями (например, контроллером и сервисом), то конвертер JPA для него **не нужен и не имеет смысла**.

---

## Почему DTO не нужно сериализовывать в базе?

- DTO — это классы, которые описывают формат передачи данных между клиентом и сервером либо между внутренними слоями приложения.
- Они либо конструируются из Entity, либо мапятся в Entity.
- Хранение в БД идёт через Entity (или `@Embeddable`), которые возможно содержат JSON-представления с конвертерами.

---

## Когда может понадобиться конвертер для DTO?

- Если вы используете DTO напрямую как Entity (что не рекомендуется).
- Если где-то DTO хранится в базе в виде JSON-строк (редкий и неэффективный случай).
- Если у вас в DTO есть поле типа `LocalConstructionContextDto`, которое должно при сохранении сериализоваться в базу — тогда возможно нужна такая же логика, но это скорее ошибка архитектуры.

---

## Выводы и рекомендации

- **Конвертеры JPA нужны для Entity-моделей и работы с базой.**
- Для DTO конвертеры не нужны — DTO должны быть простыми POJO/POKO/Kotlin data классами.
- Чтобы проблема с отображением в Swagger не была связана с конвертером — можно его не делать для DTO.
- Если ваша сложная структура внутри Entity сериализуется через конвертер — это нормально и правильно.

---

## Дополнительно

Если проект большой и сложный, хорошо отделять:

| Уровень                 | Классы                        | Особенности                                |
|------------------------|------------------------------|--------------------------------------------|
| Entity (JPA)           | `LocalConstructionContext`      | Хранение в базе, конвертеры, маппинг       |
| DTO                    | `LocalConstructionContextDto`   | Передача по API, отображение, валидация     |
| Конвертеры             | Для Entity классов              | Сериализуют/десериализуют в JSON для БД    |

---

Если же у DTO поля похожи на Entity, и вы хотите улучшить их отображение в Swagger UI или springdoc — тогда конвертеры здесь не помогут, проблема в слоях сериализации Jackson и описаниях моделей.  

---

Если хотите, могу подсказать, как лучше настроить DTO для корректного отображения в OpenAPI.

turns-00084.parquet:40470

c0776214063a5c2597bfb1b5
turn 10/19gpt-4.1-mini-2025-04-14RussianRussia427 words
degenerate_repetitionAbsentFinal dense release
USER
как в дэбагере отследить какие именно поля дто будут отрисованы в ui сваггера
ASSISTANT
Чтобы в дебагере понять, какие именно поля вашего DTO попадут в схему OpenAPI и отобразятся в Swagger UI при использовании springdoc-openapi, можно попробовать несколько способов и точек отслеживания:

---

## 1. Посмотреть итоговую OpenAPI-схему в JSON

先最直接且 полезный способ — получить сгенерированный JSON схемы по URL:

```
/v3/api-docs
```

- Там содержится чистая спецификация OpenAPI с полным описанием моделей.  
- Найдите в этом JSON нужный DTO (например, `MetaDto`) — там будет перечислен список полей, их типы, описания.  
- Если поля отсутствуют — значит springdoc для этих полей не сгенерировал схему, их в UI не будет.

---

## 2. Включить DEBUG логирование springdoc

В `application.properties` или `application.yml`:

```properties
logging.level.org.springdoc=DEBUG
logging.level.org.springdoc.webmvc=TRACE
```

- При старте приложения и генерации schemas springdoc выведет подробную информацию в логи.  
- Там можно увидеть, какие модели и поля обрабатываются.  
- Иногда springdoc логит предупреждения о невидимых или конфликтующих полях.

---

## 3. Использовать breakpoint и проставить точки останова в springdoc

Можно поставить брейкпоинт или логирование непосредственно в классах springdoc, которые отвечают за построение модели, например:

- Класс `org.springdoc.core.providers.ObjectMapperProvider` — предоставляет `ObjectMapper`
- Класс `org.springdoc.core.GenericResponseBuilder` — формирует response  
- Класс `org.springdoc.core.preset.GenericParameterBuilder` и `ModelBuilder` — отвечают за построение описания моделей

Проще всего:

- Найдите метод `io.swagger.v3.core.jackson.ModelResolver.resolve(...)` (в swagger-core) — он отвечает за построение модели из Java/Kotlin класса.  
- В breakpoint-е посмотрите, какие поля добавляются в объект `Model` для вашего DTO.  
- По шагам посмотрите, какие поля пропускаются.

---

## 4. Отладить сериализацию DTO с помощью ObjectMapper

Т.к. springdoc использует внутренне Jackson для анализа моделей, можно напрямую:

```kotlin
val objectMapper = ObjectMapper().registerKotlinModule()
val jsonSchema = objectMapper.generateJsonSchema(MetaDto::class.java)
println(jsonSchema)
```

Или вызвать:

```kotlin
val schema = ModelConverters.getInstance().read(MetaDto::class.java)
println(schema)
```

- Это покажет (в консоли или дебаге) развернутую схему по вашему классу.

---

## 5. Временное аннотирование всех полей DTO @Schema(description = "...")

Добавьте на все поля временно аннотацию `@Schema(description = "Track this field")`. Если поле не появится в UI — значит оно фильтруется или игнорируется.

---

## 6. Проверить, какие Jackson-аннотации применяются к полям DTO

- Некоторые Jackson-аннотации типа `@JsonIgnore`, `@JsonView` или модификации `ObjectMapper` могут влиять на видимость полей для springdoc.  
- В дебаггере проверьте конфигурацию ObjectMapper и контекст сериализации.

---

## Резюме

- В первую очередь исследуйте сгенерированный OpenAPI JSON по `/v3/api-docs` — там видно, что попадёт в UI  
- Логи springdoc с DEBUG уровнем помогут найти проблемы  
- Breakpoint на `ModelResolver` из swagger-core поможет отследить процесс построения модели и какие поля туда попадают  
- Проверка Jackson-аннотаций и сериализации DTO поможет понять, почему поле не отображается

---

Если хотите, могу помочь настроить пример такого дебага.

turns-00084.parquet:40471

18af324f5800b19b37f93c79
turn 11/19gpt-4.1-mini-2025-04-14RussianRussia3168 words
degenerate_repetitionAbsentFinal dense release
USER
где именно в методе resolve стоит смотреть какие поля добавляются
   public Schema resolve(AnnotatedType annotatedType, ModelConverterContext context, Iterator<ModelConverter> next) {

        boolean isPrimitive = false;
        Schema model = null;
        List<String> requiredProps = new ArrayList<>();

        if (annotatedType == null) {
            return null;
        }
        if (this.shouldIgnoreClass(annotatedType.getType())) {
            return null;
        }

        final JavaType type;
        if (annotatedType.getType() instanceof JavaType) {
            type = (JavaType) annotatedType.getType();
        } else {
            type = _mapper.constructType(annotatedType.getType());
        }

        final Annotation resolvedSchemaOrArrayAnnotation = AnnotationsUtils.mergeSchemaAnnotations(annotatedType.getCtxAnnotations(), type);
        final io.swagger.v3.oas.annotations.media.Schema resolvedSchemaAnnotation =
                resolvedSchemaOrArrayAnnotation == null ?
                        null :
                        resolvedSchemaOrArrayAnnotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
                                ((io.swagger.v3.oas.annotations.media.ArraySchema) resolvedSchemaOrArrayAnnotation).schema() :
                                (io.swagger.v3.oas.annotations.media.Schema) resolvedSchemaOrArrayAnnotation;

        final io.swagger.v3.oas.annotations.media.ArraySchema resolvedArrayAnnotation =
                resolvedSchemaOrArrayAnnotation == null ?
                        null :
                        resolvedSchemaOrArrayAnnotation instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
                                (io.swagger.v3.oas.annotations.media.ArraySchema) resolvedSchemaOrArrayAnnotation :
                                null;

        final BeanDescription beanDesc;
        {
            BeanDescription recurBeanDesc = _mapper.getSerializationConfig().introspect(type);

            HashSet<String> visited = new HashSet<>();
            JsonSerialize jsonSerialize = recurBeanDesc.getClassAnnotations().get(JsonSerialize.class);
            while (jsonSerialize != null && !Void.class.equals(jsonSerialize.as())) {
                String asName = jsonSerialize.as().getName();
                if (visited.contains(asName)) break;
                visited.add(asName);

                recurBeanDesc = _mapper.getSerializationConfig().introspect(
                        _mapper.constructType(jsonSerialize.as())
                );
                jsonSerialize = recurBeanDesc.getClassAnnotations().get(JsonSerialize.class);
            }
            beanDesc = recurBeanDesc;
        }


        String name = annotatedType.getName();
        if (StringUtils.isBlank(name)) {
            // allow override of name from annotation
            if (!annotatedType.isSkipSchemaName() && resolvedSchemaAnnotation != null && !resolvedSchemaAnnotation.name().isEmpty()) {
                name = resolvedSchemaAnnotation.name();
            }
            if (StringUtils.isBlank(name) && (type.isEnumType() || !ReflectionUtils.isSystemType(type))) {
                name = _typeName(type, beanDesc);
            }
        }

        name = decorateModelName(annotatedType, name);

        // if we have a ref, for OAS 3.0 we don't consider anything else, while for OAS 3.1 we store the ref and add it later
        String schemaRefFromAnnotation = null;
        if (resolvedSchemaAnnotation != null &&
                StringUtils.isNotEmpty(resolvedSchemaAnnotation.ref())) {
            if (resolvedArrayAnnotation == null) {
                schemaRefFromAnnotation = resolvedSchemaAnnotation.ref();
                if (!openapi31) {
                    return new JsonSchema().$ref(resolvedSchemaAnnotation.ref()).name(name);
                }
            } else {
                ArraySchema schema = new ArraySchema();
                resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
                return schema.items(new Schema().$ref(resolvedSchemaAnnotation.ref()).name(name));
            }
        }

        if (!annotatedType.isSkipOverride() && resolvedSchemaAnnotation != null && !Void.class.equals(resolvedSchemaAnnotation.implementation())) {
            Class<?> cls = resolvedSchemaAnnotation.implementation();

            LOGGER.debug("overriding datatype from {} to {}", type, cls.getName());

            Annotation[] ctxAnnotation = null;
            if (resolvedArrayAnnotation != null && annotatedType.getCtxAnnotations() != null) {
                List<Annotation> annList = new ArrayList<>();
                for (Annotation a: annotatedType.getCtxAnnotations()) {
                    if (!(a instanceof ArraySchema)) {
                        annList.add(a);
                    }
                }
                annList.add(resolvedSchemaAnnotation);
                ctxAnnotation = annList.toArray(new Annotation[annList.size()]);
            } else {
                ctxAnnotation = annotatedType.getCtxAnnotations();
            }

            AnnotatedType aType = new AnnotatedType()
                    .type(cls)
                    .ctxAnnotations(ctxAnnotation)
                    .parent(annotatedType.getParent())
                    .name(annotatedType.getName())
                    .resolveAsRef(annotatedType.isResolveAsRef())
                    .jsonViewAnnotation(annotatedType.getJsonViewAnnotation())
                    .propertyName(annotatedType.getPropertyName())
                    .components(annotatedType.getComponents())
                    .skipOverride(true);
            if (resolvedArrayAnnotation != null) {
                ArraySchema schema = new ArraySchema();
                resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
                Schema innerSchema = null;

                Schema primitive = PrimitiveType.createProperty(cls);
                if (primitive != null) {
                    innerSchema = primitive;
                } else {
                    innerSchema = context.resolve(aType);
                    if (innerSchema != null && isObjectSchema(innerSchema) && StringUtils.isNotBlank(innerSchema.getName())) {
                        // create a reference for the items
                        if (context.getDefinedModels().containsKey(innerSchema.getName())) {
                            innerSchema = new Schema().$ref(constructRef(innerSchema.getName()));
                        }
                    } else if (innerSchema != null && innerSchema.get$ref() != null) {
                        innerSchema = new Schema().$ref(StringUtils.isNotEmpty(innerSchema.get$ref()) ? innerSchema.get$ref() : innerSchema.getName());
                    }
                }
                schema.setItems(innerSchema);
                return schema;
            } else {
                Schema implSchema = context.resolve(aType);
                if (implSchema != null && aType.isResolveAsRef() && isObjectSchema(implSchema) && StringUtils.isNotBlank(implSchema.getName())) {
                    // create a reference for the items
                    if (context.getDefinedModels().containsKey(implSchema.getName())) {
                        implSchema = new Schema().$ref(constructRef(implSchema.getName()));
                    }
                } else if (implSchema != null && implSchema.get$ref() != null) {
                    implSchema = new Schema().$ref(StringUtils.isNotEmpty(implSchema.get$ref()) ? implSchema.get$ref() : implSchema.getName());
                }
                return implSchema;
            }
        }

        if (model == null && !annotatedType.isSkipOverride() && resolvedSchemaAnnotation != null &&
                StringUtils.isNotEmpty(resolvedSchemaAnnotation.type()) &&
                !resolvedSchemaAnnotation.type().equals("object")) {
            PrimitiveType primitiveType = PrimitiveType.fromTypeAndFormat(resolvedSchemaAnnotation.type(), resolvedSchemaAnnotation.format());
            if (primitiveType == null) {
                primitiveType = PrimitiveType.fromType(type);
            }
            if (primitiveType == null) {
                primitiveType = PrimitiveType.fromName(resolvedSchemaAnnotation.type());
            }
            if (primitiveType != null) {
                Schema primitive = primitiveType.createProperty();
                model = primitive;
                isPrimitive = true;

            }
        }

        if (model == null && type.isEnumType()) {
            model = new StringSchema();
            _addEnumProps(type.getRawClass(), model);
            isPrimitive = true;
        }
        if (model == null) {
            PrimitiveType primitiveType = PrimitiveType.fromType(type);
            if (primitiveType != null) {
                model = PrimitiveType.fromType(type).createProperty();
                isPrimitive = true;
            }
        }

        if (!annotatedType.isSkipJsonIdentity()) {
            JsonIdentityInfo jsonIdentityInfo = AnnotationsUtils.getAnnotation(JsonIdentityInfo.class, annotatedType.getCtxAnnotations());
            if (jsonIdentityInfo == null) {
                jsonIdentityInfo = type.getRawClass().getAnnotation(JsonIdentityInfo.class);
            }
            if (model == null && jsonIdentityInfo != null) {
                JsonIdentityReference jsonIdentityReference = AnnotationsUtils.getAnnotation(JsonIdentityReference.class, annotatedType.getCtxAnnotations());
                if (jsonIdentityReference == null) {
                    jsonIdentityReference = type.getRawClass().getAnnotation(JsonIdentityReference.class);
                }
                model = new GeneratorWrapper().processJsonIdentity(annotatedType, context, _mapper, jsonIdentityInfo, jsonIdentityReference);
                if (model != null) {
                    return model;
                }
            }
        }

        if (model == null && annotatedType.getJsonUnwrappedHandler() != null) {
            model = annotatedType.getJsonUnwrappedHandler().apply(annotatedType);
            if (model == null) {
                return null;
            }
        }

        if ("Object".equals(name)) {
            Schema schema = new Schema();
            if (schemaRefFromAnnotation != null) {
                schema.raw$ref(schemaRefFromAnnotation);
            }
            return schema;
        }

        List<Class<?>> composedSchemaReferencedClasses = getComposedSchemaReferencedClasses(type.getRawClass(), annotatedType.getCtxAnnotations(), resolvedSchemaAnnotation);
        boolean isComposedSchema = composedSchemaReferencedClasses != null;

        if (isPrimitive) {
            XML xml = resolveXml(beanDesc.getClassInfo(), annotatedType.getCtxAnnotations(), resolvedSchemaAnnotation);
            if (xml != null) {
                model.xml(xml);
            }
            applyBeanValidatorAnnotations(model, annotatedType.getCtxAnnotations(), null, false);
            resolveSchemaMembers(model, annotatedType, context, next);
            if (resolvedArrayAnnotation != null) {
                ArraySchema schema = new ArraySchema();
                resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
                schema.setItems(model);
                return schema;
            }
            if (type.isEnumType() && shouldResolveEnumAsRef(resolvedSchemaAnnotation)) {
                // Store off the ref and add the enum as a top-level model
                context.defineModel(name, model, annotatedType, null);
                // Return the model as a ref only property
                model = new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + name);
            }
            if (!isComposedSchema) {
                if (schemaRefFromAnnotation != null && model != null) {
                    model.raw$ref(schemaRefFromAnnotation);
                }
                return model;
            }
        }

        /**
         * --Preventing parent/child hierarchy creation loops - Comment 1--
         * Creating a parent model will result in the creation of child models. Creating a child model will result in
         * the creation of a parent model, as per the second If statement following this comment.
         *
         * By checking whether a model has already been resolved (as implemented below), loops of parents creating
         * children and children creating parents can be short-circuited. This works because currently the
         * ModelConverterContextImpl will return null for a class that already been processed, but has not yet been
         * defined. This logic works in conjunction with the early immediate definition of model in the context
         * implemented later in this method (See "Preventing parent/child hierarchy creation loops - Comment 2") to
         * prevent such
         */
        Schema resolvedModel = context.resolve(annotatedType);
        if (resolvedModel != null) {
            if (name != null && name.equals(resolvedModel.getName())) {
                return resolvedModel;
            }
        }

        Type jsonValueType = findJsonValueType(beanDesc);

        if(jsonValueType != null) {
            AnnotatedType aType = new AnnotatedType()
                    .type(jsonValueType)
                    .parent(annotatedType.getParent())
                    .name(annotatedType.getName())
                    .schemaProperty(annotatedType.isSchemaProperty())
                    .resolveAsRef(annotatedType.isResolveAsRef())
                    .jsonViewAnnotation(annotatedType.getJsonViewAnnotation())
                    .propertyName(annotatedType.getPropertyName())
                    .ctxAnnotations(annotatedType.getCtxAnnotations())
                    .components(annotatedType.getComponents())
                    .skipOverride(true);
            return context.resolve(aType);
        }

        if (type.isContainerType()) {
            // TODO currently a MapSchema or ArraySchema don't also support composed schema props (oneOf,..)
            isComposedSchema = false;
            JavaType keyType = type.getKeyType();
            JavaType valueType = type.getContentType();
            String pName = null;
            if (valueType != null) {
                BeanDescription valueTypeBeanDesc = _mapper.getSerializationConfig().introspect(valueType);
                pName = _typeName(valueType, valueTypeBeanDesc);
            }
            List<Annotation> strippedCtxAnnotations = new ArrayList<>();
            if (resolvedSchemaAnnotation != null) {
                strippedCtxAnnotations.add(0, resolvedSchemaAnnotation);
            }
            if (annotatedType.getCtxAnnotations() != null) {
                strippedCtxAnnotations.addAll(Arrays.stream(
                        annotatedType.getCtxAnnotations()).filter(
                        ass -> !ass.annotationType().getName().startsWith("io.swagger") && !ass.annotationType().getName().startsWith("jakarta.validation.constraints")
                ).collect(Collectors.toList()));
            }


            if (keyType != null && valueType != null) {
                if (ReflectionUtils.isSystemType(type) && !annotatedType.isSchemaProperty() && !annotatedType.isResolveAsRef()) {
                    context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(valueType).jsonViewAnnotation(annotatedType.getJsonViewAnnotation()));
                    return null;
                }
                Schema addPropertiesSchema = context.resolve(
                        new AnnotatedType()
                                .type(valueType)
                                .schemaProperty(annotatedType.isSchemaProperty())
                                .ctxAnnotations(strippedCtxAnnotations.toArray(new Annotation[0]))
                                .skipSchemaName(true)
                                .resolveAsRef(annotatedType.isResolveAsRef())
                                .jsonViewAnnotation(annotatedType.getJsonViewAnnotation())
                                .propertyName(annotatedType.getPropertyName())
                                .components(annotatedType.getComponents())
                                .parent(annotatedType.getParent()));
                if (addPropertiesSchema != null) {
                    if (StringUtils.isNotBlank(addPropertiesSchema.getName())) {
                        pName = addPropertiesSchema.getName();
                    }
                    if (isObjectSchema(addPropertiesSchema) && pName != null) {
                        // create a reference for the items
                        if (context.getDefinedModels().containsKey(pName)) {
                            addPropertiesSchema = new Schema().$ref(constructRef(pName));
                        }
                    } else if (addPropertiesSchema.get$ref() != null) {
                        addPropertiesSchema = new Schema().$ref(StringUtils.isNotEmpty(addPropertiesSchema.get$ref()) ? addPropertiesSchema.get$ref() : addPropertiesSchema.getName());
                    }
                }
                Schema mapModel = new MapSchema().additionalProperties(addPropertiesSchema);
                mapModel.name(name);
                model = mapModel;
            } else if (valueType != null) {
                if (ReflectionUtils.isSystemType(type) && !annotatedType.isSchemaProperty() && !annotatedType.isResolveAsRef()) {
                    context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(valueType).jsonViewAnnotation(annotatedType.getJsonViewAnnotation()));
                    return null;
                }
                Schema items = context.resolve(new AnnotatedType()
                        .type(valueType)
                        .schemaProperty(annotatedType.isSchemaProperty())
                        .ctxAnnotations(strippedCtxAnnotations.toArray(new Annotation[0]))
                        .skipSchemaName(true)
                        .resolveAsRef(annotatedType.isResolveAsRef())
                        .propertyName(annotatedType.getPropertyName())
                        .jsonViewAnnotation(annotatedType.getJsonViewAnnotation())
                        .components(annotatedType.getComponents())
                        .parent(annotatedType.getParent()));

                if (items == null) {
                    return null;
                }
                if (annotatedType.isSchemaProperty() && annotatedType.getCtxAnnotations() != null && annotatedType.getCtxAnnotations().length > 0) {
                    if (!"object".equals(items.getType())) {
                        for (Annotation annotation : annotatedType.getCtxAnnotations()) {
                            if (annotation instanceof XmlElement) {
                                XmlElement xmlElement = (XmlElement) annotation;
                                if (xmlElement != null && xmlElement.name() != null && !"".equals(xmlElement.name()) && !JAXB_DEFAULT.equals(xmlElement.name())) {
                                    XML xml = items.getXml() != null ? items.getXml() : new XML();
                                    xml.setName(xmlElement.name());
                                    items.setXml(xml);
                                }
                            }
                        }
                    }
                }
                if (StringUtils.isNotBlank(items.getName())) {
                    pName = items.getName();
                }
                if (isObjectSchema(items) && pName != null) {
                    // create a reference for the items
                    if (context.getDefinedModels().containsKey(pName)) {
                        items = new Schema().$ref(constructRef(pName));
                    }
                } else if (items.get$ref() != null) {
                    items = new Schema().$ref(StringUtils.isNotEmpty(items.get$ref()) ? items.get$ref() : items.getName());
                }

                Schema arrayModel =
                        new ArraySchema().items(items);
                if (_isSetType(type.getRawClass())) {
                    arrayModel.setUniqueItems(true);
                }
                arrayModel.name(name);
                model = arrayModel;
            } else {
                if (ReflectionUtils.isSystemType(type) && !annotatedType.isSchemaProperty() && !annotatedType.isResolveAsRef()) {
                    return null;
                }
            }
        } else if (isComposedSchema) {
            model = new ComposedSchema()
                    .type("object")
                    .name(name);
        } else {
            AnnotatedType aType = ReferenceTypeUtils.unwrapReference(annotatedType);
            if (aType != null) {
                model = context.resolve(aType);
                return model;
            } else {
                model = new Schema()
                        .type("object")
                        .name(name);
            }
        }

        if (!type.isContainerType() && StringUtils.isNotBlank(name)) {
            // define the model here to support self/cyclic referencing of models
            context.defineModel(name, model, annotatedType, null);
        }

        XML xml = resolveXml(beanDesc.getClassInfo(), annotatedType.getCtxAnnotations(), resolvedSchemaAnnotation);
        if (xml != null) {
            model.xml(xml);
        }

        if (!(model instanceof ArraySchema) || (model instanceof ArraySchema && resolvedArrayAnnotation == null)) {
            resolveSchemaMembers(model, annotatedType, context, next);
        }

        final XmlAccessorType xmlAccessorTypeAnnotation = beanDesc.getClassAnnotations().get(XmlAccessorType.class);

        // see if @JsonIgnoreProperties exist
        Set<String> propertiesToIgnore = resolveIgnoredProperties(beanDesc.getClassAnnotations(), annotatedType.getCtxAnnotations());

        List<Schema> props = new ArrayList<>();
        Map<String, Schema> modelProps = new LinkedHashMap<>();

        List<BeanPropertyDefinition> properties = beanDesc.findProperties();
        List<String> ignoredProps = getIgnoredProperties(beanDesc);
        properties.removeIf(p -> ignoredProps.contains(p.getName()));
        for (BeanPropertyDefinition propDef : properties) {
            Schema property = null;
            String propName = propDef.getName();
            Annotation[] annotations = null;

            AnnotatedMember member = propDef.getPrimaryMember();
            if (member == null) {
                final BeanDescription deserBeanDesc = _mapper.getDeserializationConfig().introspect(type);
                List<BeanPropertyDefinition> deserProperties = deserBeanDesc.findProperties();
                for (BeanPropertyDefinition prop : deserProperties) {
                    if (StringUtils.isNotBlank(prop.getInternalName()) && prop.getInternalName().equals(propDef.getInternalName())) {
                        member = prop.getPrimaryMember();
                        break;
                    }
                }
            }

            // hack to avoid clobbering properties with get/is names
            // it's ugly but gets around https://github.com/swagger-api/swagger-core/issues/415
            if(propDef.getPrimaryMember() != null) {
                final JsonProperty jsonPropertyAnn = propDef.getPrimaryMember().getAnnotation(JsonProperty.class);
                if (jsonPropertyAnn == null || !jsonPropertyAnn.value().equals(propName)) {
                    if (member != null) {
                        java.lang.reflect.Member innerMember = member.getMember();
                        if (innerMember != null) {
                            String altName = innerMember.getName();
                            if (altName != null) {
                                final int length = altName.length();
                                for (String prefix : Arrays.asList("get", "is")) {
                                    final int offset = prefix.length();
                                    if (altName.startsWith(prefix) && length > offset
                                            && !Character.isUpperCase(altName.charAt(offset))) {
                                        propName = altName;
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }

            PropertyMetadata md = propDef.getMetadata();

            if (member != null && !ignore(member, xmlAccessorTypeAnnotation, propName, propertiesToIgnore, propDef)) {

                List<Annotation> annotationList = new ArrayList<>();
                for (Annotation a : member.annotations()) {
                    annotationList.add(a);
                }

                annotations = annotationList.toArray(new Annotation[annotationList.size()]);

                if(hiddenByJsonView(annotations, annotatedType)) {
                    continue;
                }

                JavaType propType = member.getType();
                if(propType != null && "void".equals(propType.getRawClass().getName())) {
                    if (member instanceof AnnotatedMethod) {
                        propType = ((AnnotatedMethod)member).getParameterType(0);
                    }

                }
                String propSchemaName = null;
                io.swagger.v3.oas.annotations.media.Schema ctxSchema = AnnotationsUtils.getSchemaAnnotation(annotations);
                if (AnnotationsUtils.hasSchemaAnnotation(ctxSchema)) {
                    if (!StringUtils.isBlank(ctxSchema.name())) {
                        propSchemaName = ctxSchema.name();
                    }
                }
                io.swagger.v3.oas.annotations.media.ArraySchema ctxArraySchema = AnnotationsUtils.getArraySchemaAnnotation(annotations);
                if (propSchemaName == null) {
                    if (AnnotationsUtils.hasArrayAnnotation(ctxArraySchema)) {
                        if (AnnotationsUtils.hasSchemaAnnotation(ctxArraySchema.schema())) {
                            if (!StringUtils.isBlank(ctxArraySchema.schema().name())) {
                                propSchemaName = ctxArraySchema.schema().name();
                            }
                        }
                    }
                }
                if (StringUtils.isNotBlank(propSchemaName)) {
                    propName = propSchemaName;
                }
                Annotation propSchemaOrArray = AnnotationsUtils.mergeSchemaAnnotations(annotations, propType);
                final io.swagger.v3.oas.annotations.media.Schema propResolvedSchemaAnnotation =
                        propSchemaOrArray == null ?
                                null :
                                propSchemaOrArray instanceof io.swagger.v3.oas.annotations.media.ArraySchema ?
                                        ((io.swagger.v3.oas.annotations.media.ArraySchema) propSchemaOrArray).schema() :
                                        (io.swagger.v3.oas.annotations.media.Schema) propSchemaOrArray;

                io.swagger.v3.oas.annotations.media.Schema.AccessMode accessMode = resolveAccessMode(propDef, type, propResolvedSchemaAnnotation);
                io.swagger.v3.oas.annotations.media.Schema.RequiredMode requiredMode = resolveRequiredMode(propResolvedSchemaAnnotation);

                Annotation[] ctxAnnotation31 = null;

                if (openapi31) {
                    List<Annotation> ctxAnnotations31List = new ArrayList<>();
                    if (annotations != null) {
                        for (Annotation a : annotations) {
                            if (
                                    !(a instanceof io.swagger.v3.oas.annotations.media.Schema) &&
                                            !(a instanceof io.swagger.v3.oas.annotations.media.ArraySchema)) {
                                ctxAnnotations31List.add(a);
                            }
                        }
                        ctxAnnotation31 = ctxAnnotations31List.toArray(new Annotation[ctxAnnotations31List.size()]);
                    }
                }

                AnnotatedType aType = new AnnotatedType()
                        .type(propType)
                        .ctxAnnotations(openapi31 ? ctxAnnotation31 : annotations)
                        .parent(model)
                        .resolveAsRef(annotatedType.isResolveAsRef())
                        .jsonViewAnnotation(annotatedType.getJsonViewAnnotation())
                        .skipSchemaName(true)
                        .schemaProperty(true)
                        .components(annotatedType.getComponents())
                        .propertyName(propName);

                final AnnotatedMember propMember = member;
                aType.jsonUnwrappedHandler(t -> {
                    JsonUnwrapped uw = propMember.getAnnotation(JsonUnwrapped.class);
                    if (uw != null && uw.enabled()) {
                        t
                            .ctxAnnotations(null)
                            .jsonUnwrappedHandler(null)
                            .resolveAsRef(false);
                        handleUnwrapped(props, context.resolve(t), uw.prefix(), uw.suffix(), requiredProps);
                        return null;
                    } else {
                        return new Schema();
                    }
                });
                property = context.resolve(aType);
                property = clone(property);
                if (openapi31) {
                    Optional<Schema> reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, context);
                    if (reResolvedProperty.isPresent()) {
                        property = reResolvedProperty.get();
                    }
                    reResolvedProperty = AnnotationsUtils.getArraySchema(ctxArraySchema, annotatedType.getComponents(), null, openapi31, property);
                    if (reResolvedProperty.isPresent()) {
                        property = reResolvedProperty.get();
                    }

                }
                if (property != null) {
                    Boolean required = md.getRequired();
                    if (!io.swagger.v3.oas.annotations.media.Schema.RequiredMode.NOT_REQUIRED.equals(requiredMode)) {
                        if (required != null && !Boolean.FALSE.equals(required)) {
                            addRequiredItem(model, propName);
                        } else {
                            if (propDef.isRequired()) {
                                addRequiredItem(model, propName);
                            }
                        }
                    }
                    if (property.get$ref() == null || openapi31) {
                        if (accessMode != null) {
                            switch (accessMode) {
                                case AUTO:
                                    break;
                                case READ_ONLY:
                                    property.readOnly(true);
                                    break;
                                case READ_WRITE:
                                    break;
                                case WRITE_ONLY:
                                    property.writeOnly(true);
                                    break;
                                default:
                            }
                        }
                    }
                    final BeanDescription propBeanDesc = _mapper.getSerializationConfig().introspect(propType);
                    if (property != null && !propType.isContainerType()) {
                        if (isObjectSchema(property)) {
                            // create a reference for the property
                            String pName = _typeName(propType, propBeanDesc);
                            if (StringUtils.isNotBlank(property.getName())) {
                                pName = property.getName();
                            }

                            if (context.getDefinedModels().containsKey(pName)) {
                                property = new Schema().$ref(constructRef(pName));
                                property = clone(property);
                                if (openapi31) {
                                    Optional<Schema> reResolvedProperty = AnnotationsUtils.getSchemaFromAnnotation(ctxSchema, annotatedType.getComponents(), null, openapi31, property, context);
                                    if (reResolvedProperty.isPresent()) {
                                        property = reResolvedProperty.get();
                                    }
                                    reResolvedProperty = AnnotationsUtils.getArraySchema(ctxArraySchema, annotatedType.getComponents(), null, openapi31, property);
                                    if (reResolvedProperty.isPresent()) {
                                        property = reResolvedProperty.get();
                                    }
                                }
                            }
                        } else if (property.get$ref() != null) {
                            if (!openapi31) {
                                property = new Schema().$ref(StringUtils.isNotEmpty(property.get$ref()) ? property.get$ref() : property.getName());
                            } else {
                                if (StringUtils.isEmpty(property.get$ref())) {
                                    property.$ref(property.getName());
                                }
                            }
                        }
                    }
                    property.setName(propName);
                    JAXBAnnotationsHelper.apply(propBeanDesc.getClassInfo(), annotations, property);
                    if (property != null && io.swagger.v3.oas.annotations.media.Schema.RequiredMode.REQUIRED.equals(requiredMode)) {
                        addRequiredItem(model, property.getName());
                    }
                    final boolean applyNotNullAnnotations = io.swagger.v3.oas.annotations.media.Schema.RequiredMode.AUTO.equals(requiredMode);
                    annotations = addGenericTypeArgumentAnnotationsForOptionalField(propDef, annotations);
                    applyBeanValidatorAnnotations(propDef, property, annotations, model, applyNotNullAnnotations);

                    props.add(property);
                }
            }
        }
        for (Schema prop : props) {
            modelProps.put(prop.getName(), prop);
        }
        if (modelProps.size() > 0) {
            if (model.getProperties() == null) {
                model.setProperties(modelProps);
            } else {
                for (String key : modelProps.keySet()) {
                    model.addProperty(key, modelProps.get(key));
                }
            }

            for(String propName : requiredProps) {
                addRequiredItem(model, propName);
            }
        }

        /**
         * --Preventing parent/child hierarchy creation loops - Comment 2--
         * Creating a parent model will result in the creation of child models, as per the first If statement following
         * this comment. Creating a child model will result in the creation of a parent model, as per the second If
         * statement following this comment.
         *
         * The current model must be defined in the context immediately. This done to help prevent repeated
         * loops where  parents create children and children create parents when a hierarchy is present. This logic
         * works in conjunction with the "early checking" performed earlier in this method
         * (See "Preventing parent/child hierarchy creation loops - Comment 1"), to prevent repeated creation loops.
         *
         *
         * As an aside, defining the current model in the context immediately also ensures that child models are
         * available for modification by resolveSubtypes, when their parents are created.
         */
        if (!type.isContainerType() && StringUtils.isNotBlank(name)) {
            context.defineModel(name, model, annotatedType, null);
        }

        /**
         * This must be done after model.setProperties so that the model's set
         * of properties is available to filter from any subtypes
         **/
        if (!resolveSubtypes(model, beanDesc, context, annotatedType.getJsonViewAnnotation())) {
            model.setDiscriminator(null);
        }

        Discriminator discriminator = resolveDiscriminator(type, context);
        if (discriminator != null) {
            model.setDiscriminator(discriminator);
        }

        if (resolvedSchemaAnnotation != null) {
            String ref = resolvedSchemaAnnotation.ref();
            // consider ref as is
            if (!StringUtils.isBlank(ref)) {
                model.$ref(ref);
            }
            Class<?> not = resolvedSchemaAnnotation.not();
            if (!Void.class.equals(not)) {
                model.not((new Schema().$ref(context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(not).jsonViewAnnotation(annotatedType.getJsonViewAnnotation())).getName())));
            }
            if (resolvedSchemaAnnotation.requiredProperties() != null &&
                    resolvedSchemaAnnotation.requiredProperties().length > 0 &&
                    StringUtils.isNotBlank(resolvedSchemaAnnotation.requiredProperties()[0])) {
                for (String prop : resolvedSchemaAnnotation.requiredProperties()) {
                    addRequiredItem(model, prop);
                }
            }
        }

        Map<String, Schema> patternProperties = resolvePatternProperties(type, annotatedType.getCtxAnnotations(), context);
        if (model != null && patternProperties != null && !patternProperties.isEmpty()) {
            if (model.getPatternProperties() == null) {
                model.patternProperties(patternProperties);
            } else {
                model.getPatternProperties().putAll(patternProperties);
            }
        }


        Map<String, Schema> schemaProperties = resolveSchemaProperties(type, annotatedType.getCtxAnnotations(), context);
        if (model != null && schemaProperties != null && !schemaProperties.isEmpty()) {
            if (model.getProperties() == null) {
                model.properties(schemaProperties);
            } else {
                model.getProperties().putAll(schemaProperties);
            }
        }

        if (openapi31) {
            Map<String, Schema> dependentSchemas = resolveDependentSchemas(type, annotatedType.getCtxAnnotations(), context, annotatedType.getComponents(), annotatedType.getJsonViewAnnotation(), openapi31);
            if (model != null && dependentSchemas != null && !dependentSchemas.isEmpty()) {
                if (model.getDependentSchemas() == null) {
                    model.dependentSchemas(dependentSchemas);
                } else {
                    model.getDependentSchemas().putAll(dependentSchemas);
                }
            }
        }

        if (isComposedSchema) {

            ComposedSchema composedSchema = (ComposedSchema) model;

            Class<?>[] allOf = resolvedSchemaAnnotation.allOf();
            Class<?>[] anyOf = resolvedSchemaAnnotation.anyOf();
            Class<?>[] oneOf = resolvedSchemaAnnotation.oneOf();

            List<Class<?>> allOfFiltered = Stream.of(allOf)
                    .distinct()
                    .filter(c -> !this.shouldIgnoreClass(c))
                    .filter(c -> !(c.equals(Void.class)))
                    .collect(Collectors.toList());
            allOfFiltered.forEach(c -> {
                Schema allOfRef = context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(c).jsonViewAnnotation(annotatedType.getJsonViewAnnotation()));
                Schema refSchema = new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + allOfRef.getName());
                if (StringUtils.isBlank(allOfRef.getName())) {
                    refSchema = allOfRef;
                }
                // allOf could have already being added during subtype resolving
                if (composedSchema.getAllOf() == null || !composedSchema.getAllOf().contains(refSchema)) {
                    composedSchema.addAllOfItem(refSchema);
                }
                // remove shared properties defined in the parent
                if (isSubtype(beanDesc.getClassInfo(), c)) {
                    removeParentProperties(composedSchema, allOfRef);
                }
            });

            List<Class<?>> anyOfFiltered = Stream.of(anyOf)
                    .distinct()
                    .filter(c -> !this.shouldIgnoreClass(c))
                    .filter(c -> !(c.equals(Void.class)))
                    .collect(Collectors.toList());
            anyOfFiltered.forEach(c -> {
                Schema anyOfRef = context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(c).jsonViewAnnotation(annotatedType.getJsonViewAnnotation()));
                if (StringUtils.isNotBlank(anyOfRef.getName())) {
                    composedSchema.addAnyOfItem(new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + anyOfRef.getName()));
                } else {
                    composedSchema.addAnyOfItem(anyOfRef);
                }
                // remove shared properties defined in the parent
                if (isSubtype(beanDesc.getClassInfo(), c)) {
                    removeParentProperties(composedSchema, anyOfRef);
                }

            });

            List<Class<?>> oneOfFiltered = Stream.of(oneOf)
                    .distinct()
                    .filter(c -> !this.shouldIgnoreClass(c))
                    .filter(c -> !(c.equals(Void.class)))
                    .collect(Collectors.toList());
            oneOfFiltered.forEach(c -> {
                Schema oneOfRef = context.resolve(new AnnotatedType().components(annotatedType.getComponents()).type(c).jsonViewAnnotation(annotatedType.getJsonViewAnnotation()));
                if (oneOfRef != null) {
                    if (StringUtils.isBlank(oneOfRef.getName())) {
                        composedSchema.addOneOfItem(oneOfRef);
                    } else {
                        composedSchema.addOneOfItem(new Schema().$ref(Components.COMPONENTS_SCHEMAS_REF + oneOfRef.getName()));
                    }
                    // remove shared properties defined in the parent
                    if (isSubtype(beanDesc.getClassInfo(), c)) {
                        removeParentProperties(composedSchema, oneOfRef);
                    }
                }

            });

            if (!composedModelPropertiesAsSibling) {
                if (composedSchema.getAllOf() != null && !composedSchema.getAllOf().isEmpty()) {
                    if (composedSchema.getProperties() != null && !composedSchema.getProperties().isEmpty()) {
                        ObjectSchema propSchema = new ObjectSchema();
                        propSchema.properties(composedSchema.getProperties());
                        composedSchema.setProperties(null);
                        composedSchema.addAllOfItem(propSchema);
                    }
                }
            }
        }

        if (!type.isContainerType() && StringUtils.isNotBlank(name)) {
            // define the model here to support self/cyclic referencing of models
            context.defineModel(name, model, annotatedType, null);
        }

        if (model != null && annotatedType.isResolveAsRef() &&
            (isComposedSchema || isObjectSchema(model)) &&
            StringUtils.isNotBlank(model.getName()))
        {
            if (context.getDefinedModels().containsKey(model.getName())) {
                model = new Schema().$ref(constructRef(model.getName()));
            }
        } else if (model != null && model.get$ref() != null) {
            model = new Schema().$ref(StringUtils.isNotEmpty(model.get$ref()) ? model.get$ref() : model.getName());
        }

        if (model != null && resolvedArrayAnnotation != null) {
            if (!"array".equals(model.getType())) {
                ArraySchema schema = new ArraySchema();
                schema.setItems(model);
                resolveArraySchema(annotatedType, schema, resolvedArrayAnnotation);
                return schema;
            } else {
                if (model instanceof ArraySchema) {
                    resolveArraySchema(annotatedType, (ArraySchema) model, resolvedArrayAnnotation);
                }
            }
        }

        resolveDiscriminatorProperty(type, context, model);
        model = resolveWrapping(type, context, model);

        return model;
    } 
ASSISTANT
В методе `resolve` из swagger-core (в swagger openapi) основная логика **добавления полей в модель** происходит в этом фрагменте:

```java
List<Schema> props = new ArrayList<>();
Map<String, Schema> modelProps = new LinkedHashMap<>();

List<BeanPropertyDefinition> properties = beanDesc.findProperties();
List<String> ignoredProps = getIgnoredProperties(beanDesc);
properties.removeIf(p -> ignoredProps.contains(p.getName()));

for (BeanPropertyDefinition propDef : properties) {
    Schema property = null;
    String propName = propDef.getName();
    // ... вытаскиваем аннотации, тип, member и т.п.
    // ...
    property = context.resolve(aType); // Рекурсивно разрешаем схему поля

    if (property != null) {
        // корректируем required, readOnly, writeOnly и т.п.

        props.add(property);
    }
}

for (Schema prop : props) {
    modelProps.put(prop.getName(), prop);
}
if (modelProps.size() > 0) {
    if (model.getProperties() == null) {
        model.setProperties(modelProps);
    } else {
        for (String key : modelProps.keySet()) {
            model.addProperty(key, modelProps.get(key));
        }
    }

    for(String propName : requiredProps) {
        addRequiredItem(model, propName);
    }
}
```

---

### Что тут происходит?

- Из `BeanDescription` — в `properties` берутся все свойства класса (поля, геттеры, сеттеры), которые не игнорируются (по аннотациям типа `@JsonIgnore` и прочим фильтрам).  
- Для каждого поля вызывается `context.resolve(aType)` — это рекурсивный вызов построения схемы для этого типа поля.  
- Поле добавляется в список свойств `props`, затем все свойства собраны в мапу `modelProps`, и мапа устанавливается в итоговую схему модели `model`.

---

### Значит именно этот цикл и участок кода отвечает за то, **какие поля попадут в описание DTO в openapi**, а значит — и будут отображаться в Swagger UI.

---

### Как отладить в этом месте?

- Поставьте breakpoint внутри цикла `for (BeanPropertyDefinition propDef : properties)` — там можно:  
  - Посмотреть `propDef.getName()` (имя поля)  
  - Посмотреть `property = context.resolve(aType)` — какая схема создаётся для поля  
  - Посмотреть аннотации, фильтры, используемые для исключения или включения поля

- Проверьте, нет ли здесь условий, при которых поле пропускается (например, из-за `JsonView`, `JsonIgnore`, `Schema(hidden = true)`, или нет корректного member/геттера).

- Если поле не попадает — значит причина в Jackson BeanDescription или аннотациях.

---

### Итог

**Именно после цикла перебора `beanDesc.findProperties()` и вызова `context.resolve()` для каждого поля происходит его добавление в итоговый объект `Schema`.**

Если хотите проверить почему какое-то поле отсутствует — отслеживайте:

- Появляется ли оно в `properties`  
- Вызывается ли для него `context.resolve()`  
- Какая Schema получается в результате

---

Если хотите — могу помочь с конкретными шагами отладки и объяснить, как в Kotlin-проектах с Lombok/Sealed класами работать с этим.

turns-00084.parquet:40472

805c9b524e662e3c09e620a6
turn 12/19gpt-4.1-mini-2025-04-14RussianRussia454 words
degenerate_repetitionAbsentFinal dense release
USER
 if (modelProps.size() > 0) { ... вот на этом этапе я уже вижу не все поля для MetaDto
ASSISTANT
Если на этапе, где происходит добавление полей в `modelProps` (то есть:

```java
if (modelProps.size() > 0) {
    if (model.getProperties() == null) {
        model.setProperties(modelProps);
    } else {
        for (String key : modelProps.keySet()) {
            model.addProperty(key, modelProps.get(key));
        }
    }

    for(String propName : requiredProps) {
        addRequiredItem(model, propName);
    }
}
```

вы уже видите **не все поля вашего `MetaDto`**, то причина в том, что вызов

```java
beanDesc.findProperties()
```

в самом начале не возвращает всех ожидаемых полей. То есть, уже на уровне Jackson introspection часть полей не видна.

---

# Возможные причины отсутствия полей на уровне `beanDesc.findProperties()`:

1. **Поля не видны Jackson из-за отсутствия геттеров/сеттеров.**  
   Jackson по умолчанию анализирует Java Beans — именно геттеры-сеттеры или поля (если настроен режим field-based). В Kotlin data классах геттеры обычно есть, но если это какие-то кастомные типы или Nullable с `val`, иногда из-за пропертей Kotlin/Java reflection может что-то не подтягиваться.

2. **Аннотации `@JsonIgnore`, `@JsonView` или другие фильтры.**  
   Если на поле/геттер наложены такие аннотации, Jackson не покажет их в `beanDesc.findProperties()`.

3. **Конфигурация ObjectMapper, которая игнорирует некоторые поля.**  
   Например, установлен custom `VisibilityChecker` для игнорирования полей без геттеров, или кастомные миксины.

4. **Проблемы с Kotlin sealed class / вложенными типами и поддержкой kotlin-reflect.**  
   Если не подключён kotlin-reflect, Jackson может не совсем корректно видеть свойства класса.

5. **Использование `@JsonValue` или специфических сериализаторов, которые могут "сплющивать" объект.**

---

# Как это проверить и исправить

- В отладчике **посмотрите результат `beanDesc.findProperties()` — какие поля там реально есть?**  
  Если поле отсутствует — значит причина на уровне Jackson introspection.

- Проверьте в вашем проекте:

  - Есть ли у DTO геттеры для всех полей?  
  - Подключён ли `kotlin-reflect`?  
  - Есть ли на полях или классах аннотации типа `@JsonIgnore`?  
  - Как настроен Jackson — field or property access?  
  - Пробовали ли вы явно указать

    ```kotlin
    @JsonProperty
    val someField: String
    ```

    для проблемных полей?

- Для Kotlin data классов обычно достаточно `kotlin-reflect` и штатного jackson-module-kotlin.

---

# Рекомендации по исправлению

1. **Добавьте зависимость kotlin-reflect**

```gradle
implementation "org.jetbrains.kotlin:kotlin-reflect"
```

2. **Убедитесь, что в конфигурации ObjectMapper подключён модуль kotlin**

```kotlin
@Bean
fun objectMapper(): ObjectMapper =
    jacksonObjectMapper().registerKotlinModule()
```

3. **Проверьте Jackson аннотации, попробуйте дополнительно добавить `@JsonProperty` на подозрительные поля**

4. **Временно попробуйте заменить поля на примитивы** (например, `ValueType.StringValue` → `String`), чтобы проверить влияет ли тип поля на видимость.

5. **Проверьте также, нет ли конфликтов с `@JsonView`**

---

# Итог

Отсутствие полей в `modelProps` связано с тем, что эти поля **не появились в списке свойств от BeanDescription меньще значит, что Jackson не видит их**. Нужно проверить и устранить причину их невидимости именно в Jackson introspection, тогда springdoc корректно создаст полную схему.

---

Если хотите, могу помочь проверить конкретный класс и подсказать, что с ним именно не так с точки зрения Jackson.

turns-00084.parquet:40473

f53324d550fda0124fd6b2b2
turn 13/19gpt-4.1-mini-2025-04-14RussianRussia229 words
degenerate_repetitionAbsentFinal dense release
USER
Где обьявляется метод beanProperties
ASSISTANT
В вашем вопросе, судя по всему, под `beanProperties` подразумевается список свойств класса, которые Jackson использует для построения схемы — обычно метод, возвращающий такие свойства, называется `findProperties()` и принадлежит классу `com.fasterxml.jackson.databind.introspect.BeanDescription`.

---

### Где объявлен метод `findProperties()`?

- Метод `findProperties()` принадлежит классу:  
  `com.fasterxml.jackson.databind.introspect.BeanDescription`

- Определение метода (из jackson-databind):

```java
/**
 * Method for accessing Bean properties (usually fields and/or getters/setters),
 * as known to this introspector.
 *
 * @return List of properties, never null (but may be empty).
 */
public abstract List<BeanPropertyDefinition> findProperties();
```

- Реализация этого метода находится в классах-наследниках, которые зависят от конкретного типа Bean (например, для POJO).

---

### Как получить `BeanDescription` и вызвать `findProperties()`?

В вашем коде в springdoc (swagger-core) используется так:

```java
BeanDescription beanDesc = _mapper.getSerializationConfig().introspect(type);
List<BeanPropertyDefinition> properties = beanDesc.findProperties();
```

- `_mapper` — это экземпляр `com.fasterxml.jackson.databind.ObjectMapper`.
- `introspect(JavaType)` возвращает `BeanDescription` для данного типа.
- `findProperties()` возвращает все свойства (поля/getter/setter), которые Jackson рассматривает как свойства Java Bean.

---

### Кратко:

| Что                 | Где объявлен                                     |
|---------------------|-------------------------------------------------|
| Метод `findProperties()` | интерфейс/абстрактный класс `BeanDescription` в библиотеке Jackson Databind |

---

Если вы искали именно метод `beanProperties` — его нет в Jackson, вероятно, перепутали с `findProperties()` или аналогичным методом introspection.

---

### Ресурсы

- [Документация Jackson Databind](https://fasterxml.github.io/jackson-databind/javadoc/2.13/com/fasterxml/jackson/databind/introspect/BeanDescription.html)  
- Исходники Jackson на GitHub: https://github.com/FasterXML/jackson-databind/blob/master/src/main/java/com/fasterxml/jackson/databind/introspect/BeanDescription.java

---

Если нужно, могу помочь с примером, как вызвать `findProperties()` для класса и посмотреть список его свойств из кода.