Как десериализовать массив JSON элементов списка в Java?

#java #json #jackson

#java #json #джексон

Вопрос:

У меня есть заданный JSON:

     {
  "facility-no": "2011",
  "standard-counter": [
    {
      "id": "0",
      "type": "0",
      "text": "Gebucht",
      "free": "0",
      "present": "0",
      "percent": "100",
      "max": "0",
      "status": "Frei",
      "status-value": "0"
    }, ...
], ...
}
 

и я хочу, чтобы он десериализовался в мои классы…

Класс-оболочка:

 public class Counters {

    @JsonProperty("facility-no")
    private String facilityId;

    @JsonProperty("standard-counter")
    private List<XCounter> xCounters;
}
 

Класс, реализующий объект, хранящийся в списке в классе-оболочке:

 public class XCounter {

    protected String id;
    @JsonIgnore
    public static final CounterTypeEnum type = CounterTypeEnum.X_COUNTER;

    // standard amp; level counter properties
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    String text;
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    int free;
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    int present;
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    float percent;
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    int max;
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    String status;
    @JsonSerialize(include=JsonSerialize.Inclusion.NON_NULL)
    @JsonProperty("status-value")
    int statusValue;
    ...(all getters and setters...)
 

Вот мой счетчик:

 public enum CounterTypeEnum {
    X_COUNTER(0), Y_COUNTER(1), Z_COUNTER(2);
    private int type;
    private CounterTypeEnum(final int type) {
        this.type = type;
    }
    public int getType() {
        return this.type;
    }
}
 

Однако я всегда получаю UnrecognizedPropertyException:

 org.codehaus.jackson.map.exc.UnrecognizedPropertyException: Unrecognized field "type" (Class com.foo.bar.XCounter), not marked as ignorable
 at [Source: java.io.StringReader@42077608; line: 1, column: 61] (through reference chain: com.foo.bar.Counters["standard-counter"]->com.foo.bar.XCounter["type"])
 

если я не использую @JsonIgnoreProperties(ignoreUnknown=true) на уровне класса нестандартных счетчиков.

Как я могу избежать этого исключения без использования @JsonIgnoreProperties(ignoreUnknown=true)?

Комментарии:

1. Почему вы type не хотите десериализоваться?

2. Пожалуйста, проверьте, существует ли метод getType() получения, также он помечен @JsonProperty(«тип»)

3. Как я уже говорил вам, JSON, который я получаю, исправлен. Свойство type может быть лишь одним из нескольких значений, которые я реализовал как перечисление. В моем классе XCounter это конечное / постоянное поле, которое нельзя установить. Следовательно, я не хочу, чтобы он был десериализован. Я думаю, что это ошибка проектирования при создании JSON в виде списка различных (XCounter, YCounter, …), в то время как все они имеют свойство «type» для указания типа счетчика. Я думаю, было бы лучше иметь либо один тип (класс) с атрибутом «type», либо иметь столько классов, сколько необходимо, без необходимости иметь свойство type.

4. Геттеры и сеттеры аннотируются, как вы предложили.

5. Простой ответ: не используйте Jackson. На самом деле пишу код. Люди тратят больше времени, пытаясь заставить Джексона работать, чем потребовалось бы, чтобы «просто сделать это».

Ответ №1:

Вы можете рассмотреть возможность замены вашего типа перечисления фактическим типом класса, использующим полиморфную десериализацию Джексона. В этом случае Jackson создаст экземпляр определенного типа в зависимости от значения свойства type .

Вот пример:

 public class JacksonPolymorphic {
    public static String JSON = "{n"  
            "  "facility-no": "2011",n"  
            "  "standard-counter": [n"  
            "    {n"  
            "      "id": "0",n"  
            "      "type": "0",n"  
            "      "text": "Gebucht",n"  
            "      "free": "0",n"  
            "      "present": "0",n"  
            "      "percent": "100",n"  
            "      "max": "0",n"  
            "      "status": "Frei",n"  
            "      "status-value": "0"n"  
            "    }n"  
            "]n"  
            "}";

    @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "type")
    public static abstract class Counter {
        private final String id;

        public Counter(String id) {
            this.id = id;
        }
    }

    public static class Counters {
        private final String facilityId;
        private final List<Counter> counters;

        public Counters(@JsonProperty("facility-no") String facilityId,
                        @JsonProperty("standard-counter") List<Counter> counters) {
            this.facilityId = facilityId;
            this.counters = counters;
        }

        @Override
        public String toString() {
            return "Counters{"  
                    "facilityId='"   facilityId   '''  
                    ", counters="   counters  
                    '}';
        }
    }

    @JsonTypeName("0")
    public static class XCounter extends Counter {
        private final String text;
        private final int free;
        private final int present;
        private final float percent;
        private final int max;
        private final String status;
        private final int statusValue;

        public XCounter(@JsonProperty("id") String id,
                        @JsonProperty("text") String text,
                        @JsonProperty("free") int free,
                        @JsonProperty("present") int present,
                        @JsonProperty("percent") float percent,
                        @JsonProperty("max") int max,
                        @JsonProperty("status") String status,
                        @JsonProperty("status-value") int statusValue) {
            super(id);
            this.text = text;
            this.free = free;
            this.present = present;
            this.percent = percent;
            this.max = max;
            this.status = status;
            this.statusValue = statusValue;
        }

        @Override
        public String toString() {
            return "XCounter{"  
                    "text='"   text   '''  
                    ", free="   free  
                    ", present="   present  
                    ", percent="   percent  
                    ", max="   max  
                    ", status='"   status   '''  
                    ", statusValue="   statusValue  
                    '}';
        }
    }

    public static void main(String[] args) throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerSubtypes(XCounter.class);
        System.out.println(mapper.readValue(JSON, Counters.class));
    }

}
 

Вывод:

 Counters{facilityId='2011', counters=[XCounter{text='Gebucht', free=0, present=0, percent=100.0, max=0, status='Frei', statusValue=0}]}
 

Ответ №2:

Основная проблема здесь заключается в том, что во время десериализации де-сериализатор по умолчанию попытается сопоставить весь Json с вашим объектом. Но у него не будет подсказки относительно того, где должен быть отображен «тип».

Использование пользовательского сериализатора и десериализатора — хороший подход, если вы хотите, чтобы отображались только некоторые поля.

Надеюсь, это поможет!

Удачи

Ответ №3:

Если вы хотите, чтобы поле было десериализовано, но не сериализовано, вы должны использовать @JsonIgnore в getter и объявлении атрибута, а @JsonProperty также в setter при использовании Jackson