Java 在对象列表中迭代可选属性的更简洁的方法?

Java 在对象列表中迭代可选属性的更简洁的方法?,java,json,java-8,iterator,Java,Json,Java 8,Iterator,我有一个项目对象列表 “Items”: [ { "Identifier": { “Identity”: { “ID”: “123”, “Country” : “Japan” } }, “Color”: “Red”, “Size”: { "Units": “cm”,

我有一个项目对象列表

“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”
      }]
Item类如下所示

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
}
例如

我可以迭代列表以获取item对象的ID,但是如果不检查每个属性是否为null,我就无法创建属性对象列表。 为了填充属性列表,我必须对item对象中的所有属性进行get,并为非null的属性创建属性对象。(修改项目类不是选项)

我正试图找到一种更干净的方法来实现这一点。

让您的数据:

{
  "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"
    }
  ]
}
我建议首先创建适合您需要的适当数据结构(例如:

现在,您可以随心所欲地进行转换,为了遍历所有属性并收集不可为null的属性,我们定义
属性

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()));
    }
}
输出:

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)...
有输出

== 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);
}
但您必须对特殊情况进行编码,例如“最终数据”是什么意思(
Integer
String
,…还有什么?)以及其他特殊(可能是非标准)结构,例如
列表
。。。你将进入龙目山的世界,杰克逊

(使用反射的新输出为)


使用json序列化库并启用“仅非空字段”是否可行?另一个选项是将它们反序列化为非类型的
Map
,这将反序列化任何json对象-如果存在嵌套,
对象将是另一个
Map
,深度不限。请添加您的代码。谢谢@josejuan,我实际上正在试图弄清楚是否有可能创建映射“在不遍历所有属性和收集非nullables的情况下”@nfornicole编写大量非常简单的代码通常比编写少量复杂的代码要好得多。如果您了解结构,最好编写特定的代码。无论如何,我已使用通用版本扩展了解决方案。您是如何定义from(字符串、对象)的方法?它在(字符串名称、对象值){
和(对象对象){的
公共静态流中是可选的。。。
ID: 123
Country: Japan
Units: cm
value: 140
Color: Red
ID: 345
Country: Russia
Height: 170 cm
Weight: 90 lb
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
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()))
== 123 === 
  ID: 123
  Country: Japan
  Color: Red
  Units: cm
  value: 140
== 345 === 
  ID: 345
  Country: Russia
  Height: 170 cm
  Weight: 90 lb