因此,我尝试使用Jackson解析JSON,但由于JSON不使用直接键值对,因此出现了问题。基本上,如果我想找到“标题”,则需要找到具有“标题”值的键“ typename”,然后访问与该节点关联的“值”键以获取实际标题。并且所有JSON节点和子节点都使用相同的模式。我正在努力弄清楚如何让Jackson从中解析Java对象。我需要在Jackson解析对象之前直接修改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"
}
]
}
答案 0 :(得分:0)
您可以编写自定义反序列化器或使用@JsonAnySetter
批注。您可以通过以下方式做到这一点:
JSON
反序列化为使用POJO
批注的中间@JsonAnySetter
结构POJO
的中间结构转换为Map
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;
}
}
最终的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'}}
答案 1 :(得分:0)
我添加了新答案,因为这种方法与 使用
@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}
另请参阅: