如何解决更简洁的方法来迭代对象列表中的可选属性?
“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”
}]
项目类看起来像这样
public class Item {
//properties
private IdentifierType Identifier = null;
private String Color = null;
private DimensionType Dimensions = null;
private String Weight = null;
private String Height = null;
With corresponding getter and setters for the above attributes
}
我正在尝试从项目列表中读取项目对象并创建
的映射
属性对象就像
Public class property {
String propertyName
String propertyValue
}
例如
{
123,[ {“propertyName” : “Color”,“propertyValue”: “Red”},{“propertyName” : size,“propertyValue”: “140 cm”}],345,[ {“propertyName” : “Weight”,“propertyValue”: “90 lb”},{“propertyName” : Height,“propertyValue”: “170 cm”}]
}
我能够迭代列表以获取项目对象的 ID,但我无法在不检查每个属性是否为空的情况下创建属性对象列表。 为了填充属性列表,我必须对 item 对象中的所有属性执行 get 并为非空的属性创建属性对象。 (修改项目类不是一个选项)
我正在寻找一种更简洁的方法来做到这一点。
解决方法
让你的数据:
{
"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<>
、...,您将进入 Lombok、Jackson 的世界...
(使用反射的新输出是)
== 123 ===
ID: 123
Country: Japan
Color: Red
Units: cm
value: 140
== 345 ===
ID: 345
Country: Russia
Height: 170 cm
Weight: 90 lb
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。