Android 读取 MS Word 文件时出现 Docx4J 错误:在路径上找不到 class org.eclipse.persistence.oxm.NamespacePrefixMapper

Android Docx4J Error when reading in MS Word file: Didn't find class org.eclipse.persistence.oxm.NamespacePrefixMapper on path

我正在尝试使用 Docx4j 库来读取用户选择的 Word 文件的内容(尝试遵循 https://github.com/plutext/Docx4j4Android4 中的代码)但是,当我读取 MS Word 文件时收到以下错误:

W/System.err: log4j:ERROR Could not parse url [jar:file:/data/app/com.example.diffcheckertest-8hLouozp5QPE6U8NaxyKYA==/base.apk!/log4j.xml].
    javax.xml.parsers.ParserConfigurationException: No validating DocumentBuilder implementation available
        at org.apache.harmony.xml.parsers.DocumentBuilderFactoryImpl.newDocumentBuilder(DocumentBuilderFactoryImpl.java:61)
...
Process 20716 terminated.
    Caused by: java.lang.ClassNotFoundException: Didn't find class "org.eclipse.persistence.oxm.NamespacePrefixMapper" on path: DexPathList[[zip file "/data/app/com.example.diffcheckertest-8hLouozp5QPE6U8NaxyKYA==/base.apk"]

下面是 MainActivity.kt 中打开 MS Word 文档并出现错误的代码行(我已经导入了必要的实用程序):

import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.view.View
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity

import org.apache.commons.lang3.StringUtils;
import org.docx4j.openpackaging.packages.WordprocessingMLPackage;
import org.docx4j.openpackaging.parts.WordprocessingML.MainDocumentPart;
import java.io.BufferedReader
import java.io.InputStreamReader

class MainActivity : AppCompatActivity() {
    private var READ_IN_FILE:Int = 2; // Request code used when reading in a file


    // Asks a user to read in a file
    private fun chooseFile(view: View) {
        // Only the below specified mime types are allowed in the picker
        var selectFile = Intent(Intent.ACTION_GET_CONTENT)
        selectFile.type = "*/*"
        selectFile = Intent.createChooser(selectFile, "Choose a file")
        startActivityForResult(selectFile, READ_IN_FILE)
    }

    // After receiving a result for a launched activity...
    override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
        super.onActivityResult(requestCode, resultCode, data)

        if (requestCode == READ_IN_FILE) { // When a result has been received, check if it is the result for READ_IN_FILE
            if (resultCode == Activity.RESULT_OK) { // heck if the operation to retrieve the Activity's result is successful
                // Attempt to retrieve the file
                try {
                    var uri = data?.data // Retrieve the file's resource locator
                    var document = WordprocessingMLPackage.load(uri?.let { contentResolver.openInputStream(it) });
                    //var documentPart: MainDocumentPart = document.getMainDocumentPart();
                    Toast.makeText(this, "DiffChecker successfully read in the file :D", Toast.LENGTH_SHORT).show()
                } catch (e: Exception) { // If the app failed to attempt to retrieve the error file, throw an error alert
                    println("Exception: " + e.toString());
                    Toast.makeText(this, "Sorry, but there was an error reading in the file", Toast.LENGTH_SHORT).show()
                }
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        findViewById<Button>(R.id.file1).setOnClickListener(::chooseFile);
    }
}

下面是我的 build.gradle 代码(应用级):

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
    compileSdkVersion 29
    buildToolsVersion "29.0.2"
    defaultConfig {
        applicationId "com.example.diffcheckertest"
        minSdkVersion 22
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }

    packagingOptions {
        exclude 'META-INF/DEPENDENCIES'
        exclude 'META-INF/LICENSE'
        exclude 'META-INF/LICENSE.txt'
        exclude 'META-INF/license.txt'
        exclude 'META-INF/NOTICE'
        exclude 'META-INF/NOTICE.txt'
        exclude 'META-INF/notice.txt'
        exclude 'META-INF/ASL2.0'
        exclude("META-INF/*.kotlin_module")
    }
}

dependencies {
    implementation fileTree(include: ['*.jar'], dir: 'libs')
    implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
    implementation 'androidx.appcompat:appcompat:1.1.0'
    implementation 'androidx.core:core-ktx:1.2.0'
    implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'androidx.test.ext:junit:1.1.1'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
    implementation files('libs/activation.jar')
    implementation files('libs/additionnal.jar')
    implementation files('libs/awt-bastardised-17v8.jar')
    implementation files('libs/istack-commons-runtime-3.0.4-SNAPSHOT.jar')
    implementation files('libs/jaxb-core-2.3.0-SNAPSHOT-ANDROID.jar')
    implementation files('libs/jaxb-runtime-2.3.0-SNAPSHOT-ANDROID.jar')

    //implementation files('libs/commons-lang3-3.7.jar')
    //implementation files('libs/commons-text-1.2.jar')
    implementation 'javax.xml.stream:stax-api:1.0-2'
    implementation 'com.fasterxml:aalto-xml:1.0.0'

    implementation files('libs/docx4j-6.1.1-SNAPSHOT-shaded.jar')

}

Build 是我的 build.gradle 代码(项目级):

// Top-level build file where you can add configuration options common to all sub-projects/modules.

buildscript {
    ext.kotlin_version = '1.3.61'
    repositories {
        google()
        jcenter()

    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.5.2'
        classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
        // NOTE: Do not place your application dependencies here; they belong
        // in the individual module build.gradle files
    }
}

allprojects {
    repositories {
        google()
        jcenter()

    }
}

task clean(type: Delete) {
    delete rootProject.buildDir
}

自 Android 4.4(API 级别 19)起可用的 SAF(存储访问框架)选择文件:

// Asks a user to read in a file
private fun chooseFile(view: View) {
    // Only the below specified mime types are allowed in the picker
    var selectFile = Intent(Intent.ACTION_OPEN_DOCUMENT)
    selectFile.type = "*/*"
    selectFile.addCategory(Intent.CATEGORY_OPENABLE)
    selectFile = Intent.createChooser(selectFile, "Choose a file")
    startActivityForResult(selectFile, READ_IN_FILE)
}

"org.eclipse.persistence.oxm.NamespacePrefixMapper" 防止 ClassNotFoundException 将以下行添加到您的 gradle 文件依赖项:

implementation 'org.eclipse.persistence:eclipselink:2.7.6'

为了防止重复 class 错误,您可以排除重复 classes:

implementation ('org.eclipse.persistence:eclipselink:2.7.6'){
          exclude group: 'javax.persistence.spi', module: 'ProviderUtil'
          exclude group: 'javax.persistence', module: 'Access'
}

How to run JAXB in Android

在没有 javax.xml.bind:jaxb-api:2.2.12 依赖项的情况下,您的项目是如何工作的?它应该首先抛出 java.lang.NoClassDefFoundError: Failed resolution of: Ljavax/xml/bind/JAXBContext;

Android 不支持开箱即用的 JAXB,您必须显式添加依赖项。

implementation 'javax.xml.bind:jaxb-api:2.2.12'

我决定创建一个新项目并从头开始一切,而不是尝试您的 gradle 设置。幸运的是,我可以 运行 并毫无问题地加载任何 docx 文件。

download my sample repository并在您的系统中尝试。