0

I am trying to use the Docx4j library in order to read in the contents of Word files selected by the user (tried to follow the code in https://github.com/plutext/Docx4j4Android4) However, I receive the following error when I read in a MS Word file:

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"]

Below is the line of code in MainActivity.kt where I open the MS Word document and where the error occurs (I have imported the necessary utilities):

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);
    }
}

Below is my build.gradle code (app-level):

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 is my build.gradle code (project-level):

// 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
}
Adam Lee
  • 436
  • 1
  • 14
  • 49
  • See https://stackoverflow.com/a/31139616/1031689 for some insight – JasonPlutext Feb 14 '20 at 19:26
  • @JasonPlutext I tried adding stax and aalto to my build.gradle but unfortunately it still does not work. As the maintainer of the docx4j project, do you have a complete example Android app (current) that uses docx4j to read in the contents of an MS-Word file? – Adam Lee Feb 14 '20 at 23:24
  • @JasonPlutext I understand that you have a GitHub repository that demonstrates how docx4j can be used in an Android app, but that is in Java. Do you have a Kotlin version that works? – Adam Lee Feb 14 '20 at 23:45
  • @JasonPlutext The Android example shows an Android app creating a .docx. Can you provide an example of an Android app reading .docx selected by the user? – Adam Lee Feb 17 '20 at 22:43

2 Answers2

1

How did your project work without the javax.xml.bind:jaxb-api:2.2.12 dependency? It should throw java.lang.NoClassDefFoundError: Failed resolution of: Ljavax/xml/bind/JAXBContext; at the first place.

Android does not support JAXB out of the box, and you must add the dependency explicitly.

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

Instead of trying your gradle settings I decided to create a new project and start everything from scratch. Fortunately, I could run and load any docx files without issues.

Please download my sample repository and try in your system.

Prokash Sarkar
  • 11,723
  • 1
  • 37
  • 50
0

File selecting using SAF (Storage Access Framework) available since Android 4.4 (API level 19):

// 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)
}

To prevent ClassNotFoundException for "org.eclipse.persistence.oxm.NamespacePrefixMapper" add below line to your gradle file dependencies:

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

to prevent Duplicate class error you can exclude Duplicated 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

ygngy
  • 3,630
  • 2
  • 18
  • 29
  • I get the following error: Duplicate class javax.persistence.spi.ProviderUtil found in modules eclipselink-2.5.0.jar (org.eclipse.persistence:eclipselink:2.5.0) and javax.persistence-2.1.0.jar (org.eclipse.persistence:javax.persistence:2.1.0) – Adam Lee Feb 16 '20 at 22:36
  • Despite changing the version to 2.7.6, I still receive the following error: Duplicate class javax.persistence.Access found in modules eclipselink-2.7.6.jar (org.eclipse.persistence:eclipselink:2.7.6) and jakarta.persistence-2.2.3.jar (org.eclipse.persistence:jakarta.persistence:2.2.3) – Adam Lee Feb 16 '20 at 22:50
  • @AdamLee can you exclude duplicated classes as in my updated answer? – ygngy Feb 16 '20 at 22:52
  • @Bahman Even with the exclude statements, I still get the same duplicate class errors. To be clear, my duplicate class errors aren't just javax.persistence.Access and javax.persistence.spi.ProviderUtil, there are dozens of duplicate class errors all from javax.persistence and javax.persistence.spi. However, even when I try excluding the entire two groups (without specifying a module), I still unfortunately have duplicate class errors. – Henry Zhu Feb 16 '20 at 23:30
  • @AdamLee A sample docx reader using `docx4j`: https://github.com/plutext/AndroidDocxToHtml – ygngy Feb 16 '20 at 23:45
  • @Bahman I already stated in my question that I am trying to follow the tutorial you just linked, yet it is not working unfortunately. – Adam Lee Feb 16 '20 at 23:54