Umarshalling MonthDay spring jackson json

Попытка сделать спокойный сервис для сохранения « PeriodeEnseignement », как показано ниже:

package DomainModel.Enseignement.Notations;

import java.time.MonthDay;

import javax.persistence.Basic;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.Table;

import org.hibernate.annotations.GenericGenerator;
import org.springframework.format.annotation.DateTimeFormat;

import com.fasterxml.jackson.annotation.JsonFormat;

import DomainModel.Enseignement.UniteeEnseignement;

@Entity
@Table(name = "PERIODES_ENSEIGNEMENTS")
public class PeriodeEnseignement implements UniteeEnseignement {

    private Integer id;
    private String nom;
    private MonthDay debut;
    private MonthDay fin;

    @Id
    @Column(name = "ID")
    @GeneratedValue(generator = "UseExistingOrGenerateIdGenerator")
    @GenericGenerator(name = "UseExistingOrGenerateIdGenerator", strategy = "UsefulEntities.UseExistingOrGenerateIdGenerator")
    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @Basic
    @Column(name = "NOM")
    public String getNom() {
        return nom;
    }

    public void setNom(String nom) {
        this.nom = nom;
    }

    @JsonFormat(pattern = "dd/MM")
    @DateTimeFormat(pattern = "dd/MM")
    public MonthDay getDebut() {
        return debut;
    }

    public void setDebut(MonthDay debut) {
        this.debut = debut;
    }

    @JsonFormat(pattern = "dd/MM")
    @DateTimeFormat(pattern = "dd/MM")
    public MonthDay getFin() {
        return fin;
    }

    public void setFin(MonthDay fin) {
        this.fin = fin;
    }
}

Но что-то не так с аннотированными полями:

@JsonFormat(pattern = "dd/MM")
@DateTimeFormat(pattern = "dd/MM")

Я уже сохранил некоторые другие объекты и работает как шарм, но они не содержат атрибутов MonthDay (он отлично работает с объектами, которые содержат LocalDate).

вот мой restfulcontroller:

@RestController
public class PeriodeEnseignementServices implements GenericUniteeEnseignementService<PeriodeEnseignement> {

@RequestMapping(value = "/periodeEnseignementService/registerPeriodeEnseignementService", method = RequestMethod.POST)
    private GenericResponse registerPeriodeEnseignement(PeriodeEnseignement periodeEnseignement) {
        return this.insertNewUniteeEnseignement(periodeEnseignement, this.periodeEnseignementRepository);
    }
}

На стороне клиента я получил эту ошибку:

jquery-3.3.1.js:9600 POST http://localhost:8080/periodeEnseignementService/registerPeriodeEnseignementService 400

Итак, как правильно упорядочивать/отменять атрибуты MonthDay?

заранее спасибо

{timestamp: 1552686924075, status: 400, error: "Bad Request", errors: [{,…}, {,…}],…}
error: "Bad Request"
errors: [{,…}, {,…}]
0: {,…}
arguments: [{codes: ["periodeEnseignement.debut", "debut"], arguments: null, defaultMessage: "debut",…}]
0: {codes: ["periodeEnseignement.debut", "debut"], arguments: null, defaultMessage: "debut",…}
arguments: null
code: "debut"
codes: ["periodeEnseignement.debut", "debut"]
0: "periodeEnseignement.debut"
1: "debut"
defaultMessage: "debut"
bindingFailure: true
code: "typeMismatch"
codes: ["typeMismatch.periodeEnseignement.debut", "typeMismatch.debut", "typeMismatch.java.time.MonthDay",…]
0: "typeMismatch.periodeEnseignement.debut"
1: "typeMismatch.debut"
2: "typeMismatch.java.time.MonthDay"
3: "typeMismatch"
defaultMessage: "Failed to convert property value of type 'java.lang.String' to required type 'java.time.MonthDay' for property 'debut'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@com.fasterxml.jackson.annotation.JsonFormat @org.springframework.format.annotation.DateTimeFormat java.time.MonthDay] for value '07/02'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [07/02]"
field: "debut"
objectName: "periodeEnseignement"
rejectedValue: "07/02"
1: {,…}
arguments: [{codes: ["periodeEnseignement.fin", "fin"], arguments: null, defaultMessage: "fin", code: "fin"}]
0: {codes: ["periodeEnseignement.fin", "fin"], arguments: null, defaultMessage: "fin", code: "fin"}
arguments: null
code: "fin"
codes: ["periodeEnseignement.fin", "fin"]
0: "periodeEnseignement.fin"
1: "fin"
defaultMessage: "fin"
bindingFailure: true
code: "typeMismatch"
codes: ["typeMismatch.periodeEnseignement.fin", "typeMismatch.fin", "typeMismatch.java.time.MonthDay",…]
0: "typeMismatch.periodeEnseignement.fin"
1: "typeMismatch.fin"
2: "typeMismatch.java.time.MonthDay"
3: "typeMismatch"
defaultMessage: "Failed to convert property value of type 'java.lang.String' to required type 'java.time.MonthDay' for property 'fin'; nested exception is org.springframework.core.convert.ConversionFailedException: Failed to convert from type [java.lang.String] to type [@com.fasterxml.jackson.annotation.JsonFormat @org.springframework.format.annotation.DateTimeFormat java.time.MonthDay] for value '01/12'; nested exception is java.lang.IllegalArgumentException: Parse attempt failed for value [01/12]"
field: "fin"
objectName: "periodeEnseignement"
rejectedValue: "01/12"
message: "Validation failed for object='periodeEnseignement'. Error count: 2"
path: "/periodeEnseignementService/registerPeriodeEnseignementService"
status: 400
timestamp: 1552686924075

[РЕДАКТИРОВАТЬ #2]

После удаления @DateTimeFormat из атрибутов, добавления @RequestBody и публикации метода регистрации я получил еще одну ошибку в консоли клиента:

POST http://localhost:8080/periodeEnseignementService/registerPeriodeEnseignementService 415
send @ jquery-3.3.1.js:9600
ajax @ jquery-3.3.1.js:9206
jQuery.(anonymous function) @ jquery-3.3.1.js:9355
(anonymous) @ utils_end_file.js:13
dispatch @ jquery-3.3.1.js:5183
elemData.handle @ jquery-3.3.1.js:4991


error: "Unsupported Media Type"
message: "Content type 'application/x-www-form-urlencoded;charset=UTF-8' not supported"
path: "/periodeEnseignementService/registerPeriodeEnseignementService"
status: 415
timestamp: 1552811644305

[РЕДАКТИРОВАТЬ #3]

После перехода с post на ajax на стороне клиента для отправки запроса в формате json я получил еще одну ошибку.

error: "Bad Request"
message: "JSON parse error: Cannot construct instance of `DomainModel.Enseignement.Notations.PeriodeEnseignement` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('id=5&nom=sdsdfsfd&debut=13%2F03&fin=17%2F03'); nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot construct instance of `DomainModel.Enseignement.Notations.PeriodeEnseignement` (although at least one Creator exists): no String-argument constructor/factory method to deserialize from String value ('id=5&nom=sdsdfsfd&debut=13%2F03&fin=17%2F03')↵ at [Source: (PushbackInputStream); line: 1, column: 1]"
path: "/periodeEnseignementService/registerPeriodeEnseignementService"
status: 400
timestamp: 1552820002498

Примечание. У PeriodeEnseignement есть конструктор по умолчанию.

0
0
386
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Когда вы работаете с пакетом Java 8Time и Jackson регистрируетесь JavaTimeModule в своем ObjectMapper:

ObjectMapper mapper = new ObjectMapper();
mapper.registerModule(new JavaTimeModule());

В Spring вы можете настроить его, как показано ниже:

@Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
    Jackson2ObjectMapperBuilder builder = new Jackson2ObjectMapperBuilder();
    builder.modules(new JavaTimeModule());

    return builder;
}

РЕДАКТИРОВАТЬ
Обновите сигнатуру метода на:

public GenericResponse registerPeriodeEnseignement(@RequestBody PeriodeEnseignement periodeEnseignement)

Используйте аннотацию @RequestBody и сделайте метод public.

Я добавил класс @Configuration и поместил туда эту часть кода, метод вызывается, но все та же проблема:/

Sidi Mohamed MOULEY SLIMANE 15.03.2019 20:14

@SidiMohamedMOULEYSLIMANE, не могли бы вы показать свой пример JSON трассировки стека полезных данных и исключений? Возможно, будут какие-то детали, которые помогут найти ошибку. Ваш класс и контроллер выглядят нормально.

Michał Ziober 15.03.2019 20:35

Проблема в том, что исключение не выдается, сервер не выполняет registerPeriodeEnseignement (PeriodeEnseignement periodeEnseignement), когда он имеет « PeriodeEnseignement » в качестве параметра, и отвечает, когда это не так, я уверен, что процесс десортировки не работает, поэтому сервер не отвечает:/... была такая же проблема с LocalDate (в другом restfulcontroller), прежде чем аннотировать их с помощью JsonFormat и DateTimeFormat

Sidi Mohamed MOULEY SLIMANE 15.03.2019 21:25

@SidiMohamedMOULEYSLIMANE, какая JSON полезная нагрузка? В случае, если контроллер не срабатывает, возможно, запрос неверен.

Michał Ziober 15.03.2019 21:53

вот он из google chrome, похоже, ему не удалось преобразовать строку в MonthDay:/ как и ожидалось:/

Sidi Mohamed MOULEY SLIMANE 15.03.2019 23:00

@SidiMohamedMOULEYSLIMANE, попробуйте удалить аннотацию DateTimeFormat и оставить только JsonFormat. Похоже, Spring конвертер не справляется.

Michał Ziober 15.03.2019 23:46

@SidiMohamedMOULEYSLIMANE, взгляните на мое обновление.

Michał Ziober 15.03.2019 23:59

Я добавил pruduces = {"application/json"} и потребляет = {"application/json"}, но похоже, что это не работает (см. редактирование)

Sidi Mohamed MOULEY SLIMANE 17.03.2019 09:37

@SidiMohamedMOULEYSLIMANE, как вы публикуете сообщения на стороне клиента? См.: jQuery публикует JSON

Michał Ziober 17.03.2019 10:44

правильно, я использовал функцию post()... пошел на ajax-вызов с $.ajax, чтобы вместо этого отправить json... и теперь у меня другая проблема (смотрите Edit # 3)

Sidi Mohamed MOULEY SLIMANE 17.03.2019 11:59

Я предполагаю, что это потому, что нет конструктора по умолчанию для MonthDay, единственный доступный конструктор — это MonthDay(int month, int dayOfMonth) ! ... есть ли способ сообщить Json mashaller или unmarshaller, как создать экземпляр свойства MonthDay из строки? например, переопределение чего-либо ... или редактирование конфигурации ..

Sidi Mohamed MOULEY SLIMANE 17.03.2019 12:10

@SidiMohamedMOULEYSLIMANE, см. сообщение об исключении. Он содержит 'id=5&nom=sdsdfsfd&debut=13%2F03&fin=17%2F03'. Похоже, вы не отправляете JSON полезную нагрузку в запросе. Вместо этого это выглядит как GET. Вам нужно сделать правильный Ajax звонок. Проблема на стороне клиента.

Michał Ziober 17.03.2019 12:41
Ответ принят как подходящий

Я, наконец, решил проблему с несколькими модификациями.

1- Изменение вызова на стороне клиента с jQuery.post на вызов $.ajax, как показано здесь.

var form = $('#myForm');
var jsonFormData = getFormData(form);

$.ajax({
            url : "/path/to/web/service/controller",
            type : "POST",
            dataType : "json",
            contentType : "application/json",
            data : JSON.stringify(jsonFormData),

            complete : function() {
            },

            success : function(data) {
                alert("success !");
            },

            error : function() {
                alert("failed !");
            },
        });
    });

function getFormData($form) {
    var unindexed_array = $form.serializeArray();
    var indexed_array = {};

    $.map(unindexed_array, function(n, i) {
        indexed_array[n['name']] = n['value'];
    });

    return indexed_array;
}

2- Добавлен @RequestBody в мою процедуру на стороне службы:

@RequestMapping(value = "/periodeEnseignementService/registerPeriodeEnseignementService", method = RequestMethod.POST, produces = "application/json", consumes = "application/json")
public GenericResponse registerPeriodeEnseignement(@RequestBody PeriodeEnseignement periodeEnseignement) {
    return this.insertNewUniteeEnseignement(periodeEnseignement, this.periodeEnseignementRepository);
}

3- Нет необходимости в @DateTimeFormatter и @JsonFormat("dd/MMM") в классе POJO.

4- Пришлось настроить JsonSerializer и JsonDeserializer для java.time.MonthDay, иначе поля остались пустыми.

@Bean
    public ObjectMapper objectMapperBuilder() {
        SimpleModule msJavaTimeModule = new SimpleModule();

        msJavaTimeModule.addSerializer(MonthDay.class, new JsonSerializer<MonthDay>() {
            @Override
            public void serialize(MonthDay value, JsonGenerator gen, SerializerProvider serializers)
                    throws IOException {
                gen.writeStartObject();
                gen.writeStringField("label", value.format(DateTimeFormatter.ofPattern("dd MMM", Locale.FRANCE)));
                gen.writeEndObject();
            }
        });

        msJavaTimeModule.addDeserializer(MonthDay.class, new JsonDeserializer<MonthDay>() {

            @Override
            public MonthDay deserialize(JsonParser p, DeserializationContext ctxt)
                    throws IOException, JsonProcessingException {
                JsonNode node = p.getCodec().readTree(p);

                String content = node.asText();

                int slashIndex = content.indexOf('/');

                String dayAsString = content.substring(0, slashIndex);
                String monthAsString = content.substring(slashIndex + 1);

                int day = Integer.parseInt(dayAsString);
                int month = Integer.parseInt(monthAsString);

                return MonthDay.of(month, day);
            }
        });

        ObjectMapper objectMapper = new Jackson2ObjectMapperBuilder().modules(msJavaTimeModule).build();

        return objectMapper;
    }

Спасибо Michał Ziober за руководство!

Другие вопросы по теме