让你的数据:
{
"Items": [
{
"Identifier": {
"Identity": {
"ID": "123",
"Country": "Japan"
}
},
"Color": "Red",
"Size": {
"Units": "cm",
"value": 140
}
},
{
"Identifier": {
"Identity": {
"ID": "345",
"Country": "Russia"
}
},
"Weight": "90 lb",
"Height": "170 cm"
}
]
}
我建议首先创建一个适合您需求的适当数据结构(例如):
static class Identity {
public String ID;
public String Country;
}
static class Identifier {
public Identity Identity;
}
static class Size {
public String Units;
public Integer value;
}
static class Item {
public Identifier Identifier;
public String Color;
public Size Size;
public String Height;
public String Weight;
}
static class Data {
public List<Item> Items;
}
然后你可以使用一种健壮的方式来解析你的数据文件(流、字符串、...)
Data data = new ObjectMapper().readValue(new File("test.json"), Data.class);
现在,你可以随意变换,遍历所有属性并收集不可为空的,我们定义Property类
static class Property {
public String name;
public String value;
public Property(String name, String value) {
this.name = name;
this.value = value;
}
// make a property from a nullable value
public static Optional<Property> from(String name, Object value) {
return ofNullable(value).map(v -> new Property(name, v.toString()));
}
}
from 方法是避免if 的助手。
要收集属性,我们必须遍历所有属性:
// flatMap to properties
List<Property> properties = data
.Items.stream() // get items as stream
.flatMap(item -> // flat map all their properties you want
Stream.of( // collect all of them
ofNullable(item.Identifier) // if not null
.map(id -> id.Identity) // if not null
.map(id -> Stream.of(
from("ID", id.ID),
from("Country", id.Country)
))
.orElse(Stream.empty()),
ofNullable(item.Size) // if not null
.map(size -> Stream.of(
from("Units", size.Units),
from("value", size.value)
))
.orElse(Stream.empty()),
Stream.of(from("Color", item.Color)),
Stream.of(from("Height", item.Height)),
Stream.of(from("Weight", item.Weight))
)
.flatMap(x -> x) // flat map nested property lists
.filter(Optional::isPresent) // only presents
.map(Optional::get)
)
.collect(toList());
带输出:
ID: 123
Country: Japan
Units: cm
value: 140
Color: Red
ID: 345
Country: Russia
Height: 170 cm
Weight: 90 lb
ID 不是强制性的,但我们可以使用默认值进行分组:
Map<String, List<Property>> properties = data
.Items.stream()
.collect(groupingBy(
item -> ofNullable(item.Identifier).map(id -> id.Identity).map(id -> id.ID).orElse("no-id"),
collectingAndThen(toList(), xs -> xs
.stream()
...(same aggregation)...
现在,打印地图是
// print all
properties.forEach((id, values) -> {
System.out.printf("== %s === %n", id);
values.forEach(v -> System.out.printf(" %s: %s%n", v.name, v.value));
});
有输出
== 123 ===
ID: 123
Country: Japan
Units: cm
value: 140
Color: Red
== 345 ===
ID: 345
Country: Russia
Height: 170 cm
Weight: 90 lb
你有一个特定的契约(你定义的类),虽然你应该写更多的代码行我推荐以前的解决方案(一个一个映射每个特定类型),你的解决方案会很多更健壮。
无论如何,如果您希望此解决方案适用于任何类层次结构,您可以使用反射,只需将所有以前的 flatMap lambda 替换为:
public static Stream<Property> from(Object object) {
if (object == null)
return Stream.empty();
List<Stream<Property>> rs = new ArrayList<>();
Class<?> clazz = object.getClass();
for (Field field : clazz.getFields())
if (field.getType() == String.class || field.getType() == Integer.class)
rs.add(from(field.getName(), field.get(object)).map(Stream::of).orElse(Stream.empty()));
else
rs.add(from(field.get(object)));
return rs.stream().flatMap(x -> x);
}
新替换的聚合是
collectingAndThen(toList(), xs -> xs
.stream()
.flatMap(Property::from)
.collect(toList()))
但您必须编写特殊情况,例如 “最终数据” (Integer,String,... 还有什么?)和其他特殊情况(以及可能的非标准)结构,如List<>、Stream<>,...您将进入龙目岛、杰克逊...
(使用反射的新输出是)
== 123 ===
ID: 123
Country: Japan
Color: Red
Units: cm
value: 140
== 345 ===
ID: 345
Country: Russia
Height: 170 cm
Weight: 90 lb