如何使用 Apache POI 操作评论内容

How to manipulate content of a comment with Apache POI

我想在 Docx 文档中找到评论(不知何故,按作者或 ID...),然后创建新内容。我能够 ,但没有操纵的运气。

正如我在您的问题中链接的回答中所说,到目前为止,XWPFdocument 只会在创建时读取该包部分。既没有写入权限,也没有创建该包部分的可能性。这在 XWPFDocument.java - protected void onDocumentRead(): code line 210 中提到:“// TODO 根据 XWPFComment class 创建,扩展 POIXMLDocumentPart”。

所以直到现在我们都需要自己做这件事。我们需要为注释提供 class 扩展 POIXMLDocumentPart 并注册此关系,而不仅仅是与简单的 POIXMLDocumentPart 的关系。这样就可以进行编写 XWPFDocument 时提交的更改。

示例:

import java.io.*;

import org.apache.poi.*;
import org.apache.poi.openxml4j.opc.*;
import org.apache.xmlbeans.*;

import org.apache.poi.xwpf.usermodel.*;

import static org.apache.poi.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;

import org.openxmlformats.schemas.wordprocessingml.x2006.main.*;

import javax.xml.namespace.QName;

import java.math.BigInteger;
import java.util.GregorianCalendar;
import java.util.Locale;

public class WordChangeComments {

 public static void main(String[] args) throws Exception {

  XWPFDocument document = new XWPFDocument(new FileInputStream("WordDocumentHavingComments.docx"));

  for (POIXMLDocumentPart.RelationPart rpart : document.getRelationParts()) {
   String relation = rpart.getRelationship().getRelationshipType();
   if (relation.equals(XWPFRelation.COMMENT.getRelation())) {
    POIXMLDocumentPart part = rpart.getDocumentPart(); //this is only POIXMLDocumentPart, not a high level class extending POIXMLDocumentPart
    //provide class extending POIXMLDocumentPart for comments 
    MyXWPFCommentsDocument myXWPFCommentsDocument = new MyXWPFCommentsDocument(part.getPackagePart());
    //and registering this relation instead of only relation to POIXMLDocumentPart
    String rId = document.getRelationId(part);
    document.addRelation(rId, XWPFRelation.COMMENT, myXWPFCommentsDocument);

    //now the comments are available from the new MyXWPFCommentsDocument 
    for (CTComment ctComment : myXWPFCommentsDocument.getComments().getCommentArray()) {

     System.out.print("Comment: Id: " + ctComment.getId());
     System.out.print(", Author: " + ctComment.getAuthor());
     System.out.print(", Date: " + ctComment.getDate());
     System.out.print(", Text: ");
     for (CTP ctp : ctComment.getPArray()) {
      System.out.print(ctp.newCursor().getTextValue());
     }
     System.out.println();

     //and changings can be made which were committed while writing the XWPFDocument
     if (BigInteger.ONE.equals(ctComment.getId())) { //the second comment (Id 0 = first)
      ctComment.setAuthor("New Author");
      ctComment.setInitials("NA");
      ctComment.setDate(new GregorianCalendar(Locale.US));
      CTP newCTP = CTP.Factory.newInstance();
      newCTP.addNewR().addNewT().setStringValue("The new Text for Comment with Id 1.");
      ctComment.setPArray(new CTP[]{newCTP });
     }
    }
   }
  }

  document.write(new FileOutputStream("WordDocumentHavingComments.docx"));

  document.close();
 }

//a wrapper class for the CommentsDocument /word/comments.xml in the *.docx ZIP archive
 private static class MyXWPFCommentsDocument extends POIXMLDocumentPart {

  private CTComments comments;

  private MyXWPFCommentsDocument(PackagePart part) throws Exception {
   super(part);
   comments = CommentsDocument.Factory.parse(part.getInputStream(), DEFAULT_XML_OPTIONS).getComments();
  }

  private CTComments getComments() {
   return comments;
  }

  @Override
  protected void commit() throws IOException {

System.out.println("============MyXWPFCommentsDocument is committed=================");

   XmlOptions xmlOptions = new XmlOptions(DEFAULT_XML_OPTIONS);
   xmlOptions.setSaveSyntheticDocumentElement(new QName(CTComments.type.getName().getNamespaceURI(), "comments"));
   PackagePart part = getPackagePart();
   OutputStream out = part.getOutputStream();
   comments.save(out, xmlOptions);
   out.close();
  }

 }

}

这适用于 apache poi 3.17。由于 apache poi 4.0.0 ooxml 部分是分开的。所以必须有:

...
import org.apache.poi.ooxml.*;
...
import static org.apache.poi.ooxml.POIXMLTypeLoader.DEFAULT_XML_OPTIONS;
...