使用 Jackson 在同一文件中使用未知键名处理多个 JSON 对象
Working with multiple JSON objects in the same file with unknown key names using Jackson
正在为处理实体建筑的应用程序构建模型。
理想情况下,我们想要这样的东西:
城市有多个办公室,办公室有多个房间,房间有属性。
我们正在使用 jackson 来解析从 API 数据源接收到的 JSON 有效负载,它最终看起来与我看到的示例有点不同。
我们得到的格式是:
{
"CityName1":
{ "OfficeName1":
[
{"name": RoomName1, "RoomProperty2": RoomValue1},
{"name": RoomName2, "RoomProperty2": RoomValue2}
]
},
{ "OfficeName2": [{...}]},
{ "OfficeNameX" : [{...}] },
"CityName2": {...},
"CityNameN": {...}}
Java classes:
public class City {
private Map<String, Object> additionalProperties = new HashMap<String, Object();
private List<Office> _offices = new ArrayList<Office>();
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value)
throws IOException {
_cityName = name;
String officeJson = _mapper.writeValueAsString(value);
StringBuilder sb = new StringBuilder(officeJson);
_offices.add(_mapper.readValue(officeJson, Office.class));
this.additionalProperties.put(name, value);
}
}
public class Office {
private String _officeName;
private static final ObjectMapper _mapper = new ObjectMapper();
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
private List<Room> _rooms = new ArrayList<Room>();
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value)
throws IOException {
_officeName = name;
String roomJson = _mapper.writeValueAsString(value);
Room[] rooms = _mapper.readValue(roomJson, Room[].class);
_rooms.addAll(Arrays.asList(rooms));
this.additionalProperties.put(name, value);
}
public List<Room> getRooms() {
return _rooms;
}
public void setRooms(List<Room> rooms) {
_rooms = rooms;
}
}
public class Room {
private static final String NAME = "name";
private static final String PROP_2 = "RoomProperty2";
@JsonProperty(PROP_2)
private String _propertyTwo;
@JsonProperty(NAME)
private String name;
@JsonProperty(PROP_2)
public String getPropertyTwo() {
return _propertyTwo;
}
@JsonProperty(PROP_2)
public void setPropertyTwo(String propTwo) {
_propertyTwo = propTwo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
那么我将如何用 jackson 解析它?目前,我正在使用 @JsonAnySetter 获取名称,并将其保存为城市或办公室名称,然后将发送到 JsonAnySetter 的值发送到适当的嵌套 class。真正的问题在于获取城市中的办公室列表。当使用 mapper.readvalues(String, Office.class) 时,我得到了每个城市的最后一个办公室的迭代器。有什么想法吗?
抱歉,如果这看起来令人困惑!很乐意回答我提出的任何问题。
感谢您的帮助!
我认为最好的解决方案是在此处编写您自己的 deserialiser,因为您的 JSON 文档并不能很好地映射到您想要的 class 结构。
下面的解决方案将每个城市读取为 Map<String, List<Room>>
,将城市集合读取为 Map<String, City>
,然后在反序列化器中从这些对象中创建 City
和 Office
对象.
Room.java和你的一样,下面是:
Cities.java:
@JsonDeserialize(using=CitiesDeserializer.class)
public class Cities implements Iterable<City> {
private final List<City> cities;
public Cities(final List<City> cities) {
this.cities = cities;
}
public Cities() {
this.cities = new ArrayList<>();
}
public List<City> getCities() {
return cities;
}
@Override
public Iterator<City> iterator() {
return cities.iterator();
}
}
CitiesDeserialiser.java:
public class CitiesDeserializer extends JsonDeserializer<Cities> {
private static final TypeReference<Map<String, City>> TYPE_REFERENCE = new TypeReference<Map<String, City>>() {};
@Override
public Cities deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
final Map<String, City> map = jp.readValueAs(TYPE_REFERENCE);
List<City> cities = new ArrayList<>();
for(Map.Entry<String, City> entry : map.entrySet()) {
City city = entry.getValue();
city.setName(entry.getKey());
cities.add(city);
}
return new Cities(cities);
}
}
City.java:
@JsonDeserialize(using=CityDeserialzer.class)
public class City {
private String name;
private List<Office> offices;
// Setters and getters
}
CityDeserializer.java:
public class CityDeserialzer extends JsonDeserializer<City> {
private static final TypeReference<Map<String, List<Room>>> TYPE_REFERENCE = new TypeReference<Map<String, List<Room>>>() {};
@Override
public City deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
final Map<String, List<Room>> map = jp.readValueAs(TYPE_REFERENCE);
List<Office> offices = new ArrayList<>();
for(Map.Entry<String, List<Room>> entry : map.entrySet()) {
Office office = new Office();
office.setName(entry.getKey());
office.setRooms(entry.getValue());
offices.add(office);
}
City city = new City();
city.setOffices(offices);
return city;
}
}
Office.java:
public class Office {
private String name;
private List<Room> rooms;
// Setters and getters
}
这是一个证明它有效的测试:
JSON:
{
"CityName1": {
"OfficeName1": [ {
"name": "RoomName1",
"RoomProperty2": "RoomValue1"
}, {
"name": "RoomName2",
"RoomProperty2": "RoomValue2"
} ],
"OfficeName2": [ {
"name": "RoomName3",
"RoomProperty2": "RoomValue3"
}, {
"name": "RoomName4",
"RoomProperty2": "RoomValue4"
} ]
},
"CityName2": {
"OfficeName3": [ {
"name": "RoomName5",
"RoomProperty2": "RoomValue5"
}, {
"name": "RoomName6",
"RoomProperty2": "RoomValue6"
} ],
"OfficeName4": [ {
"name": "RoomName7",
"RoomProperty2": "RoomValue7"
}, {
"name": "RoomName8",
"RoomProperty2": "RoomValue8"
} ]
}
}
Test.java:
public class Test {
public static void main(String[] args) {
String json = ...
ObjectMapper mapper = new ObjectMapper();
Cities cities = mapper.readValue(json, Cities.class);
for(City city : cities) {
System.out.println(city.getName());
for(Office office : city.getOffices()) {
System.out.println("\t" + office.getName());
for(Room room : office.getRooms()) {
System.out.println("\t\t" + room.getName());
System.out.println("\t\t\t" + room.getPropertyTwo());
}
}
}
}
}
输出:
CityName1
OfficeName1
RoomName1
RoomValue1
RoomName2
RoomValue2
OfficeName2
RoomName3
RoomValue3
RoomName4
RoomValue4
CityName2
OfficeName3
RoomName5
RoomValue5
RoomName6
RoomValue6
OfficeName4
RoomName7
RoomValue7
RoomName8
RoomValue8
正在为处理实体建筑的应用程序构建模型。
理想情况下,我们想要这样的东西:
城市有多个办公室,办公室有多个房间,房间有属性。
我们正在使用 jackson 来解析从 API 数据源接收到的 JSON 有效负载,它最终看起来与我看到的示例有点不同。
我们得到的格式是:
{
"CityName1":
{ "OfficeName1":
[
{"name": RoomName1, "RoomProperty2": RoomValue1},
{"name": RoomName2, "RoomProperty2": RoomValue2}
]
},
{ "OfficeName2": [{...}]},
{ "OfficeNameX" : [{...}] },
"CityName2": {...},
"CityNameN": {...}}
Java classes:
public class City {
private Map<String, Object> additionalProperties = new HashMap<String, Object();
private List<Office> _offices = new ArrayList<Office>();
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value)
throws IOException {
_cityName = name;
String officeJson = _mapper.writeValueAsString(value);
StringBuilder sb = new StringBuilder(officeJson);
_offices.add(_mapper.readValue(officeJson, Office.class));
this.additionalProperties.put(name, value);
}
}
public class Office {
private String _officeName;
private static final ObjectMapper _mapper = new ObjectMapper();
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
private List<Room> _rooms = new ArrayList<Room>();
@JsonAnyGetter
public Map<String, Object> getAdditionalProperties() {
return this.additionalProperties;
}
@JsonAnySetter
public void setAdditionalProperty(String name, Object value)
throws IOException {
_officeName = name;
String roomJson = _mapper.writeValueAsString(value);
Room[] rooms = _mapper.readValue(roomJson, Room[].class);
_rooms.addAll(Arrays.asList(rooms));
this.additionalProperties.put(name, value);
}
public List<Room> getRooms() {
return _rooms;
}
public void setRooms(List<Room> rooms) {
_rooms = rooms;
}
}
public class Room {
private static final String NAME = "name";
private static final String PROP_2 = "RoomProperty2";
@JsonProperty(PROP_2)
private String _propertyTwo;
@JsonProperty(NAME)
private String name;
@JsonProperty(PROP_2)
public String getPropertyTwo() {
return _propertyTwo;
}
@JsonProperty(PROP_2)
public void setPropertyTwo(String propTwo) {
_propertyTwo = propTwo;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
那么我将如何用 jackson 解析它?目前,我正在使用 @JsonAnySetter 获取名称,并将其保存为城市或办公室名称,然后将发送到 JsonAnySetter 的值发送到适当的嵌套 class。真正的问题在于获取城市中的办公室列表。当使用 mapper.readvalues(String, Office.class) 时,我得到了每个城市的最后一个办公室的迭代器。有什么想法吗?
抱歉,如果这看起来令人困惑!很乐意回答我提出的任何问题。
感谢您的帮助!
我认为最好的解决方案是在此处编写您自己的 deserialiser,因为您的 JSON 文档并不能很好地映射到您想要的 class 结构。
下面的解决方案将每个城市读取为 Map<String, List<Room>>
,将城市集合读取为 Map<String, City>
,然后在反序列化器中从这些对象中创建 City
和 Office
对象.
Room.java和你的一样,下面是:
Cities.java:
@JsonDeserialize(using=CitiesDeserializer.class)
public class Cities implements Iterable<City> {
private final List<City> cities;
public Cities(final List<City> cities) {
this.cities = cities;
}
public Cities() {
this.cities = new ArrayList<>();
}
public List<City> getCities() {
return cities;
}
@Override
public Iterator<City> iterator() {
return cities.iterator();
}
}
CitiesDeserialiser.java:
public class CitiesDeserializer extends JsonDeserializer<Cities> {
private static final TypeReference<Map<String, City>> TYPE_REFERENCE = new TypeReference<Map<String, City>>() {};
@Override
public Cities deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
final Map<String, City> map = jp.readValueAs(TYPE_REFERENCE);
List<City> cities = new ArrayList<>();
for(Map.Entry<String, City> entry : map.entrySet()) {
City city = entry.getValue();
city.setName(entry.getKey());
cities.add(city);
}
return new Cities(cities);
}
}
City.java:
@JsonDeserialize(using=CityDeserialzer.class)
public class City {
private String name;
private List<Office> offices;
// Setters and getters
}
CityDeserializer.java:
public class CityDeserialzer extends JsonDeserializer<City> {
private static final TypeReference<Map<String, List<Room>>> TYPE_REFERENCE = new TypeReference<Map<String, List<Room>>>() {};
@Override
public City deserialize(final JsonParser jp, final DeserializationContext ctxt) throws IOException {
final Map<String, List<Room>> map = jp.readValueAs(TYPE_REFERENCE);
List<Office> offices = new ArrayList<>();
for(Map.Entry<String, List<Room>> entry : map.entrySet()) {
Office office = new Office();
office.setName(entry.getKey());
office.setRooms(entry.getValue());
offices.add(office);
}
City city = new City();
city.setOffices(offices);
return city;
}
}
Office.java:
public class Office {
private String name;
private List<Room> rooms;
// Setters and getters
}
这是一个证明它有效的测试:
JSON:
{
"CityName1": {
"OfficeName1": [ {
"name": "RoomName1",
"RoomProperty2": "RoomValue1"
}, {
"name": "RoomName2",
"RoomProperty2": "RoomValue2"
} ],
"OfficeName2": [ {
"name": "RoomName3",
"RoomProperty2": "RoomValue3"
}, {
"name": "RoomName4",
"RoomProperty2": "RoomValue4"
} ]
},
"CityName2": {
"OfficeName3": [ {
"name": "RoomName5",
"RoomProperty2": "RoomValue5"
}, {
"name": "RoomName6",
"RoomProperty2": "RoomValue6"
} ],
"OfficeName4": [ {
"name": "RoomName7",
"RoomProperty2": "RoomValue7"
}, {
"name": "RoomName8",
"RoomProperty2": "RoomValue8"
} ]
}
}
Test.java:
public class Test {
public static void main(String[] args) {
String json = ...
ObjectMapper mapper = new ObjectMapper();
Cities cities = mapper.readValue(json, Cities.class);
for(City city : cities) {
System.out.println(city.getName());
for(Office office : city.getOffices()) {
System.out.println("\t" + office.getName());
for(Room room : office.getRooms()) {
System.out.println("\t\t" + room.getName());
System.out.println("\t\t\t" + room.getPropertyTwo());
}
}
}
}
}
输出:
CityName1
OfficeName1
RoomName1
RoomValue1
RoomName2
RoomValue2
OfficeName2
RoomName3
RoomValue3
RoomName4
RoomValue4
CityName2
OfficeName3
RoomName5
RoomValue5
RoomName6
RoomValue6
OfficeName4
RoomName7
RoomValue7
RoomName8
RoomValue8