Попробуйте получить поле офисного слова с помощью apache poi, но НЕ работает

Я учусь использовать Apache Poi для получения полей из документа Word. Мой демонстрационный код выглядит следующим образом, большинство из них сгенерировано ChatGPT, я вношу небольшую модификацию, но он НЕ работает; снимок для демо-слова также выглядит следующим образом

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;

import java.io.FileInputStream;
import java.util.List;

public class FindSimpleFieldInDocx {
    public static void main(String[] args) {
        try {

            FileInputStream fis = new FileInputStream("/Users/pengbo/word/template1.docx");
            XWPFDocument document = new XWPFDocument(fis);
            List<XWPFParagraph> paragraphs = document.getParagraphs();
            for (XWPFParagraph para : paragraphs) {
                System.out.println(para.getText());
                XmlCursor cursor = para.getCTP().newCursor();
                cursor.selectPath("./*");
                while (cursor.toNextSelection()) {
                    XmlCursor.TokenType tokenType = cursor.currentTokenType();
                    if (tokenType.equals(XmlCursor.TokenType.START)) {
                        if (CTSimpleField.type.getName().getLocalPart().equals(cursor.getName().getLocalPart())) {
                            //cannot reach here........
                            CTSimpleField simpleField = (CTSimpleField) cursor.getObject();
                            String fieldCode = simpleField.getInstr();
                            
                            System.out.println("find out CTSimpleField: " + fieldCode);
                        }
                    }
                }
                cursor.dispose();
            }
            document.close();
            fis.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

снимок template1.docxфайл документа template1.docx

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>5.2.2</version>
        </dependency>

        <!-- Apache POI OOXML -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>5.2.2</version>
        </dependency>

        <!-- Apache POI OOXML Schemas -->
        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml-schemas</artifactId>
            <version>4.1.2</version> <!-- 或使用与你的POI版本匹配的最新版本 -->
        </dependency>

        <!-- XMLBeans -->
        <dependency>
            <groupId>org.apache.xmlbeans</groupId>
            <artifactId>xmlbeans</artifactId>
            <version>5.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.17.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.apache.logging.log4j/log4j-api -->
        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-api</artifactId>
            <version>2.17.2</version>
        </dependency>

кто-нибудь может предоставить мне демонстрационный код, как получить поле офисного слова через текстовый документ? спасибо.

template1.docx Не размещайте ссылку на изображение документа. Киньте ссылку на сам документ

g00se 05.06.2024 16:13

Код, созданный ChatGPT, не работает. Почему я не удивлен?

Sören 05.06.2024 16:14

прикрепленный файл; Хотя он создан ChatGPT, но я дважды проверил, не обнаружил никаких проблем; в любом случае, можете ли вы привести пример того, как получить поле из документа Word.

dominic_peng 06.06.2024 01:52

poi-ooxml-schemas 4.1.2 и poi-ooxml 5.2.2 не сочетаются друг с другом. Apache POI 5 требует poi-ooxml-lite вместо poi-ooxml-schemas. Таким образом, poi-ooxml 5.2.2 и poi-ooxml-lite 5.2.2 подходят друг другу.

Axel Richter 06.06.2024 18:54
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
Как вычислять биты и понимать побитовые операторы в Java - объяснение с примерами
В компьютерном программировании биты играют важнейшую роль в представлении и манипулировании данными на двоичном уровне. Побитовые операции...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Полный курс Java для разработчиков веб-сайтов и приложений
Полный курс Java для разработчиков веб-сайтов и приложений
Получите сертификат Java Web и Application Developer, используя наш курс.
0
4
71
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Сравнивать имена элементов XML с именами типов схемы XmlObject — не лучший вариант. Для простого поля имя локальной части элемента XML — fldSimple, а имя типа схемы — CT_SimpleField. Это не соответствует.

Если вы действительно хотите найти CTSimpleField, вы ищете объекты, которые являются экземплярами CTSimpleField: cursor.getObject() instanceof CTSimpleField:

...
                if (tokenType.equals(XmlCursor.TokenType.START)) {
                    if (cursor.getObject() instanceof CTSimpleField) {
                        //current object is instance of CTSimpleField
                        CTSimpleField simpleField = (CTSimpleField) cursor.getObject();
                        String fieldCode = simpleField.getInstr();
                        
                        System.out.println("find out CTSimpleField: " + fieldCode);
                    }
                }
...

Это должно найти поля, хранящиеся как простое поле.

Но текущий Microsoft Word не хранит все поля как простые поля. В этом можно убедиться, разархивировав *.docx ZIP-архив и заглянув в /word/document.xml. Для меня XML для поля даты выглядит, например, так:

<w:p >
 <w:r>
  <w:fldChar w:fldCharType = "begin"/>
 </w:r>
 <w:r>
  <w:instrText xml:space = "preserve"> DATE \* MERGEFORMAT </w:instrText>
 </w:r>
 <w:r>
  <w:fldChar w:fldCharType = "separate"/>
 </w:r>
 <w:r>
  <w:t>05.06.2024</w:t>
 </w:r>
 <w:r>
  <w:fldChar w:fldCharType = "end"/>
 </w:r>
</w:p>

Чтобы получить это с помощью Apache POI, необходим другой подход.

В следующем полном примере показано, как получить поля CTSimpleField и CTFldChar.

import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFRun;
import org.apache.xmlbeans.XmlCursor;
import org.apache.xmlbeans.XmlObject;
import org.apache.xmlbeans.SimpleValue;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTSimpleField;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.CTFldChar;
import org.openxmlformats.schemas.wordprocessingml.x2006.main.STFldCharType;

import java.io.FileInputStream;

public class FindFieldsInDocx {
    
    static void findAndPrintCTSimpleFields(XWPFParagraph paragraph) {
        XmlCursor cursor = paragraph.getCTP().newCursor();
        String declareNameSpaces = "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' ";
        cursor.selectPath(declareNameSpaces + ".//w:fldSimple");
        while (cursor.toNextSelection()) {
            if (cursor.getObject() instanceof CTSimpleField) {
                //current object is instance of CTSimpleField
                CTSimpleField simpleField = (CTSimpleField) cursor.getObject();
                System.out.println(simpleField.getInstr() + " | " + ((SimpleValue)simpleField).getStringValue());
            }
        }   
    }
    
    static void findAndPrintCTFldCharFields(XWPFParagraph paragraph) {
        StringBuilder runFieldContent = new StringBuilder();
        boolean fldCharFound = false;
        for (XWPFRun run : paragraph.getRuns()) {
            if (containsFldChar(run, STFldCharType.BEGIN)) {
                fldCharFound = true;
            }
            if (fldCharFound) {
                runFieldContent.append(((SimpleValue)run.getCTR()).getStringValue());
                runFieldContent.append(" | ");
            }
            if (fldCharFound && containsFldChar(run, STFldCharType.END)) {
                fldCharFound = false;           
                runFieldContent.append(((SimpleValue)run.getCTR()).getStringValue());
                System.out.println(runFieldContent);
                runFieldContent = new StringBuilder();
            }
        }   
    }
    
    static boolean containsFldChar(XWPFRun run, STFldCharType.Enum type) {
        XmlCursor cursor = run.getCTR().newCursor();
        String declareNameSpaces = "declare namespace w='http://schemas.openxmlformats.org/wordprocessingml/2006/main' ";
        cursor.selectPath(declareNameSpaces + ".//w:fldChar");
        while (cursor.toNextSelection()) {
            if (cursor.getObject() instanceof CTFldChar) {
                CTFldChar fldChar = (CTFldChar) cursor.getObject();
                if (fldChar.getFldCharType() == type) {
                    return true;
                }
            }
        }
        return false;
    }
    
    public static void main(String[] args) {
        try ( FileInputStream fis = new FileInputStream("./template1.docx");
              XWPFDocument document = new XWPFDocument(fis);) {
            for (XWPFParagraph paragraph : document.getParagraphs()) {
                findAndPrintCTSimpleFields(paragraph);
                findAndPrintCTFldCharFields(paragraph);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Я изменил свой код в соответствии с вашим предложением, но, похоже, НЕ работает, он ВСЕ ЕЩЕ не может определить NUMPAGES * MERGEFORMAT. код ключа как if (cursor.getObject() instanceof CTSimpleField) { //current object is instance of CTSimpleField CTSimpleField simpleField = (CTSimpleField) cursor.getObject(); String fieldCode = simpleField.getInstr(); }my word файл как файл @Axel Richter

dominic_peng 06.06.2024 03:04

Ваш код работает, спасибо.

dominic_peng 09.06.2024 03:11

Другие вопросы по теме