Jackon JSON для анализа объекта с ключом внутри поля имени узла - PullRequest
0 голосов
/ 07 февраля 2019

Итак, я пытаюсь проанализировать JSON с помощью Jackson, но у меня возникают проблемы, потому что JSON не использует прямые пары ключ-значение.В основном, если я хочу найти «title», мне нужно найти ключ «typename» со значением «title», а затем получить доступ к ключу «value», связанному с этим узлом, чтобы получить фактический заголовок.И этот же шаблон используется со всеми узлами и подузлами JSON.Я изо всех сил пытаюсь выяснить, как заставить Джексона разобрать Java-объект из этого.Нужно ли изменять JSON напрямую, прежде чем Джексон сможет проанализировать объект?

Вот пример из файла JSON:

        {"fields":[
            {
                "typeName":"title",
                "multiple":false,
                "typeClass":"primitive",
                "value":"Shapefile Dataset"
            },
            {
                "typeName":"author",
                "multiple":true,
                "typeClass":"compound",
                "value":[
                    {
                        "authorName":{
                            "typeName":"authorName",
                            "multiple":false,
                            "typeClass":"primitive",
                            "value":"Quigley, Elizabeth"
                        },
                        "authorAffiliation":{
                            "typeName":"authorAffiliation",
                            "multiple":false,
                            "typeClass":"primitive",
                            "value":"Harvard University"
                        }
                    }
                ]
            },
            {
                "typeName":"datasetContact",
                "multiple":true,
                "typeClass":"compound",
                "value":[
                    {
                        "datasetContactName":{
                            "typeName":"datasetContactName",
                            "multiple":false,
                            "typeClass":"primitive",
                            "value":"Quigley, Elizabeth"
                        },
                        "datasetContactAffiliation":{
                            "typeName":"datasetContactAffiliation",
                            "multiple":false,
                            "typeClass":"primitive",
                            "value":"Harvard University"
                        },
                        "datasetContactEmail":{
                            "typeName":"datasetContactEmail",
                            "multiple":false,
                            "typeClass":"primitive",
                            "value":"equigley@iq.harvard.edu"
                        }
                    }
                ]
            },
            {
                "typeName":"dsDescription",
                "multiple":true,
                "typeClass":"compound",
                "value":[
                    {
                        "dsDescriptionValue":{
                            "typeName":"dsDescriptionValue",
                            "multiple":false,
                            "typeClass":"primitive",
                            "value":"Dataset for shapefile"
                        }
                    }
                ]
            },
            {
                "typeName":"subject",
                "multiple":true,
                "typeClass":"controlledVocabulary",
                "value":[
                    "Earth and Environmental Sciences"
                ]
            },
            {
                "typeName":"depositor",
                "multiple":false,
                "typeClass":"primitive",
                "value":"Quigley, Elizabeth"
            },
            {
                "typeName":"dateOfDeposit",
                "multiple":false,
                "typeClass":"primitive",
                "value":"2015-07-13"
            }
        ]
    }

Ответы [ 2 ]

0 голосов
/ 12 февраля 2019

Я добавил новый ответ, потому что этот подход полностью отличается от первого ответа, где используется аннотация @JsonAnySetter.

Существует также другой способ, которым мы можем проанализировать данные JSON,Использование Jackson полиморфных аннотаций для обработки типов: JsonTypeInfo и JsonSubTypes.Для этого нам нужно создать иерархию, которая будет представлять primitive, compound и controlledVocabulary типы.

@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "typeClass")
@JsonSubTypes({
        @JsonSubTypes.Type(value = PrimitiveField.class, name = "primitive"),
        @JsonSubTypes.Type(value = CompoundField.class, name = "compound"),
        @JsonSubTypes.Type(value = ControlledVocabularyField.class, name = "controlledVocabulary")
})
class Field<T> {
    protected String typeName;
    protected boolean multiple;
    protected T value;

    public String getTypeName() {
        return typeName;
    }

    public void setTypeName(String typeName) {
        this.typeName = typeName;
    }

    public boolean isMultiple() {
        return multiple;
    }

    public void setMultiple(boolean multiple) {
        this.multiple = multiple;
    }

    public T getValue() {
        return value;
    }

    public void setValue(T value) {
        this.value = value;
    }

    @Override
    public String toString() {
        return getClass().getSimpleName() + "{" +
                "typeName='" + typeName + '\'' +
                ", multiple=" + multiple +
                ", value=" + value +
                '}';
    }
}

class PrimitiveField extends Field<String> {
}

class CompoundField extends Field<List<Map<String, Field>>> {

    public Collection<Field> getFields() {
        if (value == null || value.isEmpty()) {
            return Collections.emptyList();
        }

        // Assume there is always one element
        Map<String, Field> object = value.get(0);

        return object.values();
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        sb.append("CompoundField{typeName='").append(typeName).append(", value=");
        getFields().forEach(sb::append);
        sb.append("}");

        return sb.toString();
    }
}

class ControlledVocabularyField extends Field<List<String>> {
}

Мы можем протестировать вышеуказанное решение, как показано ниже:

import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;

public class JsonApp {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("path to json").getAbsoluteFile();


        ObjectMapper mapper = new ObjectMapper();
        Fields fields = mapper.readValue(jsonFile, Fields.class);
        System.out.println(fields);
    }
}

class Fields {
    private List<Field> fields;

    public List<Field> getFields() {
        return fields;
    }

    public void setFields(List<Field> fields) {
        this.fields = fields;
    }

    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder();
        fields.forEach(i -> sb.append(i).append(System.lineSeparator()));
        return sb.toString();
    }
}

Над кодом напечатаны:

PrimitiveField{typeName='title', multiple=false, value=Shapefile Dataset}
CompoundField{typeName='author, value=PrimitiveField{typeName='authorName', multiple=false, value=Quigley, Elizabeth}PrimitiveField{typeName='authorAffiliation', multiple=false, value=Harvard University}}
CompoundField{typeName='datasetContact, value=PrimitiveField{typeName='datasetContactName', multiple=false, value=Quigley, Elizabeth}PrimitiveField{typeName='datasetContactAffiliation', multiple=false, value=Harvard University}PrimitiveField{typeName='datasetContactEmail', multiple=false, value=equigley@iq.harvard.edu}}
CompoundField{typeName='dsDescription, value=PrimitiveField{typeName='dsDescriptionValue', multiple=false, value=Dataset for shapefile}}
ControlledVocabularyField{typeName='subject', multiple=true, value=[Earth and Environmental Sciences]}
PrimitiveField{typeName='depositor', multiple=false, value=Quigley, Elizabeth}
PrimitiveField{typeName='dateOfDeposit', multiple=false, value=2015-07-13}

См. Также:

  1. Аннотации Джексона
0 голосов
/ 10 февраля 2019

Вы можете написать собственный десериализатор или использовать аннотацию @JsonAnySetter.Вы можете сделать это следующим образом:

  1. Десериализация JSON в середину POJO структура, которая использует @JsonAnySetter аннотацию
  2. Преобразование средней POJO структуры в Map
  3. Преобразование Map в место назначения POJO структура.

Десериализационная часть и преобразование в Map может выглядеть следующим образом:

class Fields {
    private Field[] fields;

    public Field[] getFields() {
        return fields;
    }

    public void setFields(Field[] fields) {
        this.fields = fields;
    }

    public Map<String, Object> toMap() {
        Map<String, Object> map = new HashMap<>();
        for (Field field : fields) {
            map.put(field.getTypeName(), field.getFieldValue().resolve());
        }

        return map;
    }

    @Override
    public String toString() {
        return "Fields{" +
                "fields=" + Arrays.toString(fields) +
                '}';
    }
}

class Field {

    private String typeName;
    private FieldValue fieldValue;

    @JsonAnySetter
    private void setValue(String propertyName, Object value) {
        FieldValueBuilder builder = new FieldValueBuilder();
        if (builder.accept(propertyName)) {
            this.fieldValue = builder.build(propertyName, value);
        }
    }

    public String getTypeName() {
        return typeName;
    }

    public void setTypeName(String typeName) {
        this.typeName = typeName;
    }

    public FieldValue getFieldValue() {
        return fieldValue;
    }

    public void setFieldValue(FieldValue fieldValue) {
        this.fieldValue = fieldValue;
    }

    @Override
    public String toString() {
        return "Field{" +
                "typeName='" + typeName + '\'' +
                ", fieldValue=" + fieldValue +
                '}';
    }
}

class FieldValueBuilder {

    private List<String> ignoreFields = Arrays.asList("multiple", "typeClass");

    public boolean accept(String propertyName) {
        return !ignoreFields.contains(propertyName);
    }

    public FieldValue build(String propertyName, Object value) {
        if (value instanceof String) {
            return new StringFieldValue(value.toString());
        }
        if (value instanceof List) {
            return deserialiseList((List) value);
        }

        System.out.println("Need to parse: key = " + propertyName + ", value = " + value);

        return null;
    }

    private FieldValue deserialiseList(List list) {
        if (list.isEmpty()) {
            return null;
        }

        // It is a tricky part. From example it looks like that value is always a single-element-array.
        // If not, handle it.
        Object item = list.get(0);

        if (item instanceof String) {
            return new StringFieldValue(item.toString());
        } else if (item instanceof Map) {
            List<Field> fields = new ArrayList<>();
            Map<String, Object> map = (Map<String, Object>) item;
            for (Object valueItem : map.values()) {
                if (valueItem instanceof Map) {
                    Map<String, Object> mapItem = (Map<String, Object>) valueItem;
                    Field field = new Field();
                    field.setTypeName(mapItem.get("typeName").toString());
                    field.setFieldValue(build("value", mapItem.get("value")));
                    fields.add(field);
                }
            }

            return new ListFieldValues(fields);
        } else {
            System.out.println(item);
        }

        return new NullFieldValue();
    }
}

interface FieldValue {

    Object resolve();
}

class StringFieldValue implements FieldValue {

    private final String value;

    public StringFieldValue(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    @Override
    public Object resolve() {
        return value;
    }

    @Override
    public String toString() {
        return "StringFieldValue{" +
                "value='" + value + '\'' +
                '}';
    }
}

class ListFieldValues implements FieldValue {
    private final List<Field> fields;

    public ListFieldValues(List<Field> fields) {
        this.fields = fields;
    }

    public List<Field> getFields() {
        return fields;
    }

    @Override
    public Object resolve() {
        Map<String, Object> map = new HashMap<>();
        for (Field field : fields) {
            map.put(field.getTypeName(), field.getFieldValue().resolve());
        }

        return map;
    }

    @Override
    public String toString() {
        return "ListFieldValues{" +
                "fields=" + fields +
                '}';
    }
}

class NullFieldValue implements FieldValue {

    @Override
    public Object resolve() {
        return null;
    }
}

Final POJO структура может выглядеть следующим образом:

class Book {
    private String subject;
    private Author author;
    private Description dsDescription;
    private String dateOfDeposit;
    private String depositor;
    private String title;
    private Contact datasetContact;

    public String getSubject() {
        return subject;
    }

    public void setSubject(String subject) {
        this.subject = subject;
    }

    public Author getAuthor() {
        return author;
    }

    public void setAuthor(Author author) {
        this.author = author;
    }

    public Description getDsDescription() {
        return dsDescription;
    }

    public void setDsDescription(Description dsDescription) {
        this.dsDescription = dsDescription;
    }

    public String getDateOfDeposit() {
        return dateOfDeposit;
    }

    public void setDateOfDeposit(String dateOfDeposit) {
        this.dateOfDeposit = dateOfDeposit;
    }

    public String getDepositor() {
        return depositor;
    }

    public void setDepositor(String depositor) {
        this.depositor = depositor;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public Contact getDatasetContact() {
        return datasetContact;
    }

    public void setDatasetContact(Contact datasetContact) {
        this.datasetContact = datasetContact;
    }

    @Override
    public String toString() {
        return "Book{" +
                "subject='" + subject + '\'' +
                ", author=" + author +
                ", dsDescription=" + dsDescription +
                ", dateOfDeposit='" + dateOfDeposit + '\'' +
                ", depositor='" + depositor + '\'' +
                ", title='" + title + '\'' +
                ", datasetContact=" + datasetContact +
                '}';
    }
}

class Author {
    private String authorName;
    private String authorAffiliation;

    public String getAuthorName() {
        return authorName;
    }

    public void setAuthorName(String authorName) {
        this.authorName = authorName;
    }

    public String getAuthorAffiliation() {
        return authorAffiliation;
    }

    public void setAuthorAffiliation(String authorAffiliation) {
        this.authorAffiliation = authorAffiliation;
    }

    @Override
    public String toString() {
        return "Author{" +
                "authorName='" + authorName + '\'' +
                ", authorAffiliation='" + authorAffiliation + '\'' +
                '}';
    }
}

class Description {
    private String dsDescriptionValue;

    public String getDsDescriptionValue() {
        return dsDescriptionValue;
    }

    public void setDsDescriptionValue(String dsDescriptionValue) {
        this.dsDescriptionValue = dsDescriptionValue;
    }

    @Override
    public String toString() {
        return "Description{" +
                "dsDescriptionValue='" + dsDescriptionValue + '\'' +
                '}';
    }
}

class Contact {
    private String datasetContactEmail;
    private String datasetContactAffiliation;
    private String datasetContactName;

    public String getDatasetContactEmail() {
        return datasetContactEmail;
    }

    public void setDatasetContactEmail(String datasetContactEmail) {
        this.datasetContactEmail = datasetContactEmail;
    }

    public String getDatasetContactAffiliation() {
        return datasetContactAffiliation;
    }

    public void setDatasetContactAffiliation(String datasetContactAffiliation) {
        this.datasetContactAffiliation = datasetContactAffiliation;
    }

    public String getDatasetContactName() {
        return datasetContactName;
    }

    public void setDatasetContactName(String datasetContactName) {
        this.datasetContactName = datasetContactName;
    }

    @Override
    public String toString() {
        return "Contact{" +
                "datasetContactEmail='" + datasetContactEmail + '\'' +
                ", datasetContactAffiliation='" + datasetContactAffiliation + '\'' +
                ", datasetContactName='" + datasetContactName + '\'' +
                '}';
    }
}

Пример использования кода выше:

import com.fasterxml.jackson.annotation.JsonAnySetter;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;

import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class JsonTest {

    public static void main(String[] args) throws Exception {
        File jsonFile = new File("./resource/test.json").getAbsoluteFile();


        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY);

        Fields fields = mapper.readValue(jsonFile, Fields.class);
        System.out.println(fields);
        Map<String, Object> map = fields.toMap();
        System.out.println(map);
        System.out.println(mapper.convertValue(map, Book.class));
    }
}

Печать над кодом:

Fields{fields=[Field{typeName='title', fieldValue=StringFieldValue{value='Shapefile Dataset'}}, Field{typeName='author', fieldValue=ListFieldValues{fields=[Field{typeName='authorName', fieldValue=StringFieldValue{value='Quigley, Elizabeth'}}, Field{typeName='authorAffiliation', fieldValue=StringFieldValue{value='Harvard University'}}]}}, Field{typeName='datasetContact', fieldValue=ListFieldValues{fields=[Field{typeName='datasetContactName', fieldValue=StringFieldValue{value='Quigley, Elizabeth'}}, Field{typeName='datasetContactAffiliation', fieldValue=StringFieldValue{value='Harvard University'}}, Field{typeName='datasetContactEmail', fieldValue=StringFieldValue{value='equigley@iq.harvard.edu'}}]}}, Field{typeName='dsDescription', fieldValue=ListFieldValues{fields=[Field{typeName='dsDescriptionValue', fieldValue=StringFieldValue{value='Dataset for shapefile'}}]}}, Field{typeName='subject', fieldValue=StringFieldValue{value='Earth and Environmental Sciences'}}, Field{typeName='depositor', fieldValue=StringFieldValue{value='Quigley, Elizabeth'}}, Field{typeName='dateOfDeposit', fieldValue=StringFieldValue{value='2015-07-13'}}]}
{author={authorName=Quigley, Elizabeth, authorAffiliation=Harvard University}, subject=Earth and Environmental Sciences, dsDescription={dsDescriptionValue=Dataset for shapefile}, dateOfDeposit=2015-07-13, depositor=Quigley, Elizabeth, title=Shapefile Dataset, datasetContact={datasetContactEmail=equigley@iq.harvard.edu, datasetContactAffiliation=Harvard University, datasetContactName=Quigley, Elizabeth}}
Book{subject='Earth and Environmental Sciences', author=Author{authorName='Quigley, Elizabeth', authorAffiliation='Harvard University'}, dsDescription=Description{dsDescriptionValue='Dataset for shapefile'}, dateOfDeposit='2015-07-13', depositor='Quigley, Elizabeth', title='Shapefile Dataset', datasetContact=Contact{datasetContactEmail='equigley@iq.harvard.edu', datasetContactAffiliation='Harvard University', datasetContactName='Quigley, Elizabeth'}}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...