Java 尝试访问XSSFShape的父级会导致NullPointerException

Java 尝试访问XSSFShape的父级会导致NullPointerException,java,apache-poi,Java,Apache Poi,在尝试时,我想通过检查XSSFPictures的父项来区分XSSFShapeGroup的子项和非子项: 私有void traverseShapeContainer(ShapeContainer容器){ for(XSSFShape:container){ //这里没有提到其他类型的XSSFShape。 if(XSSFPicture的形状实例){ XSSFPicture图片=(XSSFPicture)形状; System.out.println(shape.getParent()instanceof

在尝试时,我想通过检查
XSSFPictures
的父项来区分
XSSFShapeGroup
的子项和非子项:

私有void traverseShapeContainer(ShapeContainer容器){
for(XSSFShape:container){
//这里没有提到其他类型的XSSFShape。
if(XSSFPicture的形状实例){
XSSFPicture图片=(XSSFPicture)形状;
System.out.println(shape.getParent()instanceof XSSFShapeGroup);//始终输出false
System.out.println(Objects.isNull(shape.getParent());//始终输出true
}else if(XSSFShapeGroup的形状实例){
XSSFShapeGroup shapeGroup=(XSSFShapeGroup)形状;
//访问XSSFShapeGroup的内部内容
traverseShapeContainer(形状组);
}
}
}
无论
XSSFPicture
是否为
XSSFShapeGroup
的子级,这对于每种情况都是相同的

在我执行了这两个测试来检查它的父项之后,这看起来特别奇怪

  • 测试1:检查ShapeContainer是否是XSSFShapeGroup的实例
System.out.println(Objects.isNull(XSSFShapeGroup的容器实例));//输出:false
  • 测试2:在访问子节点后查找父标记
if(XSSFPicture的形状实例){
XSSFPicture图片=(XSSFPicture)形状;
//“xdr:grpSp”是XSSFShapeGroup的标记
System.out.println(picture.getCTPicture().getDomNode().getParentNode().getNodeName()
.equals(“xdr:grpSp”);//输出:true
}
这清楚地表明父级确实存在,并且允许我们在过程中检查父级

我还没有检查其他类型的
xssfshape
,例如
XSSFSimpleShape
XSSFConnector
。然而,由于它们都继承了相同的类,即,
XSSFShape
,我想结果不会有太大的不同


因此,
XSSFShape.getParent()
可能有什么问题,或者我对这个问题的看法不正确吗?

这是因为到目前为止(
apache-poi
的不完整性(2021年5月,版本
apache-poi 5.0.0
)。它影响形状组中的所有形状

XSSFShape.getParent
只返回
XSSFShape
的类成员
XSSFShapeGroup parent
。但是,在解析
Office Open XML
绘图时,apache poi只执行以下操作:

...
 } else if (obj instanceof CTGroupShape) {
  shape = new XSSFShapeGroup(this, (CTGroupShape) obj);
 }...

XSSFShapeGroup
的构造函数就是这样做的

protected XSSFShapeGroup(XSSFDrawing drawing, CTGroupShape ctGroup) {
 this.drawing = drawing;
 this.ctGroup = ctGroup;
}

因此,这缺少遍历该组中的所有形状并将其父对象设置为该形状组。因此
parent
总是
null

因此,要么使用低级类获取父类,正如您在问题中已经显示的那样。或者,更好的做法是,在方法
traverseShapeContainer(ShapeContainer container)
中检查
容器,该容器是所有已遍历形状的父容器。您可以获取这是
XSSFShapeGroup
还是
XSSFDrawing

像这样:

...
 ... void traverseShapeContainer(ShapeContainer<XSSFShape> container) {
  for (XSSFShape shape : container) { 
   // possible:   XSSFConnector, XSSFGraphicFrame, XSSFPicture, XSSFShapeGroup, XSSFSimpleShape
   if (shape instanceof XSSFConnector) {
    XSSFConnector connector = (XSSFConnector)shape;
    System.out.println(connector);

   } else if (shape instanceof XSSFGraphicFrame) {
    XSSFGraphicFrame graphicFrame = (XSSFGraphicFrame)shape;
    System.out.println(graphicFrame);

   } else if (shape instanceof XSSFPicture) {
    XSSFPicture picture = (XSSFPicture)shape;
    System.out.println(picture);
    if (container instanceof XSSFDrawing) {
     System.out.println("Picture is in drawing directly.");
    } else if (container instanceof XSSFShapeGroup) {
     System.out.println("Picture is in shape group.");
     XSSFShapeGroup parent = (XSSFShapeGroup) container;
     System.out.println("Parent is " +  parent);
    }

   } else if (shape instanceof XSSFShapeGroup) { //we have a shape group
    XSSFShapeGroup shapeGroup = (XSSFShapeGroup)shape;
    System.out.println(shapeGroup);

    traverseShapeContainer(shapeGroup); // we now can sinply get the XSSFShapeGroup as ShapeContainer<XSSFShape>

   } else if (shape instanceof XSSFSimpleShape) {
    XSSFSimpleShape simpleShape = (XSSFSimpleShape)shape;
    System.out.println(simpleShape);

   }
  }

 }
...
。。。
... void traverseShapeContainer(ShapeContainer容器){
对于(XSSFShape:container){
//可能:XSSFConnector、XSSFGraphicFrame、XSSFPicture、XSSFShapeGroup、XSSFSimpleShape
if(XSSFConnector的形状实例){
XSSFConnector连接器=(XSSFConnector)形状;
系统输出打印LN(连接器);
}else if(XSSFGraphicFrame的形状实例){
XSSFGraphicFrame graphicFrame=(XSSFGraphicFrame)形状;
System.out.println(图形框架);
}else if(XSSFPicture的形状实例){
XSSFPicture图片=(XSSFPicture)形状;
系统输出打印项次(图片);
if(XSSFDrawing的容器实例){
System.out.println(“图片直接在绘图中”);
}else if(XSSFShapeGroup的容器实例){
System.out.println(“图片在形状组中”);
XSSFShapeGroup父级=(XSSFShapeGroup)容器;
System.out.println(“父项为”+父项);
}
}else如果(XSSFShapeGroup的shape instanceof){//我们有一个shape组
XSSFShapeGroup shapeGroup=(XSSFShapeGroup)形状;
System.out.println(shapeGroup);
traverseShapeContainer(shapeGroup);//我们现在可以将XSSFShapeGroup用作ShapeContainer
}else if(XSSFSimpleShape的形状实例){
XSSFSimpleShape simpleShape=(XSSFSimpleShape)形状;
System.out.println(simpleShape);
}
}
}
...

也可以通知维护人员。@dodobird:这样做;-)。由于多种原因,我不会这样做。但是ApachePOI开发者社区的一些成员也阅读并参与了本文。