#spring-boot #spring-mvc #jackson #json-deserialization #xml-deserialization
Вопрос:
У меня есть такой DTO:
package ...;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlElementWrapper;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlRootElement;
import lombok.*;
import lombok.experimental.FieldDefaults;
import java.time.LocalDateTime;
import java.util.List;
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
@JacksonXmlRootElement(localName = "root")
public class RootDto {
@JsonInclude(value = JsonInclude.Include.NON_NULL)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime time;
@JsonInclude(value = JsonInclude.Include.NON_NULL)
@JacksonXmlElementWrapper(localName = "times")
@JacksonXmlProperty(localName = "time")
List<InternalTimeDto> times;
...
}
package ...;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.dataformat.xml.annotation.JacksonXmlProperty;
import lombok.*;
import lombok.experimental.FieldDefaults;
import java.time.LocalDateTime;
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class InternalTimeDto {
@JacksonXmlProperty(isAttribute = true)
Long id;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime time;
}
Насколько я понимаю, проблема в том, что time
поле и элементы times
поля имеют одно и то же имя. Есть ли способ разрешить конфликт, не переименовывая time
его во что-то другое? Теоретически, конфликта вообще не должно быть, так time
как поле и элементы списка находятся на разных уровнях.
Пример XML:
<root>
<time>2020-11-18 12:34</time>
<times>
<time id="5">2020-11-18 10:00</time>
<time id="6">2020-11-17 15:30</time>
</times>
...
</root>
Я нашел пару ответов, но они решают проблему, когда два разных объекта с одинаковыми именами находятся на одном уровне.
Также используется JSON:
{
"time": "2020-11-18 12:34",
"times": [
{
"id": 5,
"time": "2020-11-18 10:00"
},
{
"id": 6,
"time": "2020-11-17 15:30"
}
],
...
}
Ответ №1:
Один из способов решить проблему стирания конфликта из-за множественного определенного свойства time
, сохраняющего ваш формат xml, — это создать Times
класс, в который будут переноситься ваши List<InternalTimeDto>
времена :
public class Times {
@JacksonXmlProperty(localName = "time")
@JacksonXmlElementWrapper(useWrapping = false)
List<InternalTimeDto> times;
}
Таким образом, ваш RootDto
класс может быть переписан таким образом:
@Getter
@Setter
@Builder
@NoArgsConstructor
@AllArgsConstructor
@ToString
@FieldDefaults(level = AccessLevel.PRIVATE)
@JacksonXmlRootElement(localName = "root")
public class RootDto {
@JsonInclude(value = JsonInclude.Include.NON_NULL)
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime time;
@JsonInclude(value = JsonInclude.Include.NON_NULL)
private Times times; //<-- times field instead of List<InternalTimeDto> times
}
Вашему InternalTimeDto
классу нужна JacksonXmlText
аннотация для time
поля:
@Getter
@Setter
@Builder
@ToString
@NoArgsConstructor
@AllArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class InternalTimeDto {
@JacksonXmlProperty(isAttribute = true)
Long id;
@JacksonXmlText
@JsonFormat(pattern = "yyyy-MM-dd HH:mm")
LocalDateTime time;
}
Комментарии:
1. Спасибо, теперь это отлично работает для XML. К сожалению, чтобы все было просто, я не упомянул, что также используется JSON. Я обновил вопрос.
2. @YaroslavTarasenko Добро пожаловать. Использование моих классов В случае json в
RootDto
замене класса Times наList<InternalTimeDto> times
, в противном случае, если вы хотите использовать один и тот же код для xml и json, единственной альтернативой является написание пользовательского десериализатора дляTimes
класса.3. Да, похоже, что пользовательский десериализатор-единственное решение в моем случае. Спасибо.