Android Json文件解析及带关键字文件解压

Some problems encountered and their answers in work (Android Chapter)


Android 如何解析一个.json文件

Android中,我们通常使用JSON格式来与后端服务器通信,需要解析JSON数据来提取有用的信息。解析JSON数据通常分为两个阶段:首先将JSON数据解析为JSON对象或JSON数组,然后从中提取所需的信息。
 以下是一个简单的示例,演示如何解析一个本地JSON文件:
 假设我们有一个名为"example.json"的本地JSON文件,它包含以下内容:
json
{
  "name": "John Smith",
  "age": 35,
  "isMarried": true,
  "hobbies": [
    "reading",
    "fishing",
    "hiking"
  ]
}
我们可以使用Android中的JSON API来解析这个文件。以下是一些示例代码,假设我们希望从JSON文件中提取姓名和年龄:
import org.json.JSONObject
import java.io.BufferedReader
import java.io.InputStreamReader
 // 读取JSON文件并创建JSON对象
val inputStream = resources.assets.open("example.json")
val bufferedReader = BufferedReader(InputStreamReader(inputStream))
val jsonString = bufferedReader.use { it.readText() }
val jsonObject = JSONObject(jsonString)
 // 从JSON对象中提取所需信息
val name = jsonObject.getString("name")
val age = jsonObject.getInt("age")
在上述代码片段中,我们首先打开example.json文件,并逐行读取它的内容。然后,我们使用JSON字符串创建一个JSON对象,然后从中提取出需要的数据。在本例中,我们使用了getString()getInt()方法来提取字符串和整数类型的值。这些方法的参数是JSON键(key),它们指定需要提取的值所在的位置。
 请注意,这个示例假设example.json文件位于app/src/main/assets/目录下。如果文件位于其他目录下,需要相应地更改路径。

在/storage/7EE2-7093/目录下有一个partNumber.json文件,此json文件的内容为:

{
 "partnum": [{
   "value": "703000555AA"
  },
  {
   "value": "703000555AG"
  },
  {
   "value": "703000555AT"
  },
  {
   "value": "703000555AH"
  }
 ]
}
在Android程序中我如何解析此json文件获取到每个value后面的值
1. 读取partNumber.json文件的内容并转换为字符串。
try {
    InputStream is = new FileInputStream(new File("/storage/7EE2-7093/partNumber.json"));
    int size = is.available();
    byte buffer[] = new byte[size];
    is.read(buffer);
    is.close();
    String json = new String(buffer, "UTF-8");
} catch (IOException e) {
    e.printStackTrace();
}
2. 解析JSON字符串并获取value对应的值。
try {
    JSONObject jsonObject = new JSONObject(json);
    JSONArray partNumArray = jsonObject.getJSONArray("partnum");
    for (int i = 0; i < partNumArray.length(); i++) {
        JSONObject partNumObject = partNumArray.getJSONObject(i);
        String value = partNumObject.getString("value");
        // 在这里对每个value进行操作
    }
} catch (JSONException e) {
    e.printStackTrace();
}
在上面的代码中,先将partNumber.json文件的内容读取为字符串,然后使用JSONObject将其转换为JSON对象。接着,获取JSON对象中的partnum数组,并遍历该数组中的每个元素。对于每个元素,使用getJSONObject方法获取包含value字段的JSON对象,并使用getString方法获取value对应的值。最后,可以对每个value进行操作或将其存储到一个数组或列表中。
 注意要在AndroidManifest.xml文件中添加读取外部存储权限:  `<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />`

有一个parNum.json文件需要存放在Android Studio项目中并读取他,应该如何存放并读取

要在Android Studio项目中存储和读取parNum.json文件,可以将该文件放置在项目的assets目录中。
 1. 创建assets目录:
如果项目中没有assets目录,可以手动创建一个。在项目根目录中右键单击,选择New -> Folder -> Assets Folder,然后命名为“assets”并点击Finish2. 存储parNum.json文件:
将parNum.json文件复制到assets目录中。在资源管理器中找到该文件,右键单击并选择Copy,然后在Android Studio中的assets目录中右键单击并选择Paste即可。
 3. 读取parNum.json文件:
要读取该文件,可以使用以下代码:
import android.content.Context;
import java.io.IOException;
import java.io.InputStream;
import org.json.JSONException;
import org.json.JSONObject;
 public class Utils {
    public static JSONObject getJSON(Context context, String filename) {
        String jsonString;
        try {
            InputStream is = context.getAssets().open(filename);
            int size = is.available();
            byte[] buffer = new byte[size];
            is.read(buffer);
            is.close();
            jsonString = new String(buffer, "UTF-8");
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
        try {
            JSONObject jsonObject = new JSONObject(jsonString);
            return jsonObject;
        } catch (JSONException e) {
            e.printStackTrace();
            return null;
        }
    }
}
这个 `getJSON()` 方法将从传入的上下文中的assets目录中获取文件名对应的文件,并返回一个包含JSON数据的JSONObject对象。在您的活动中使用该方法,例如:
import android.os.Bundle;
import androidx.appcompat.app.AppCompatActivity;
import org.json.JSONObject;
 public class MainActivity extends AppCompatActivity {
    private JSONObject parNum;
     @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
         parNum = Utils.getJSON(this, "parNum.json");
        if (parNum == null) {
            // 处理获取json文件失败的情况
        } else {
            // 处理获取json文件成功的情况
        }
    }
}
这样,您就可以将parNum.json文件存储在Android Studio项目中并从中读取它了。

在Android Studio名叫ReadJsonTest项目中创建一个jsonFile的文件夹,文件夹内存放一个partNum.json的文件,在代码中如何读取partNum.json文件

您可以使用以下代码在Android Studio中读取名为partNum.json的文件:
try {
    InputStream is = getAssets().open("jsonFile/partNum.json");
     int size = is.available();
    byte[] buffer = new byte[size];
    is.read(buffer);
    is.close();
     String json = new String(buffer, "UTF-8");
     // 处理 json 字符串...
} catch (IOException ex) {
    ex.printStackTrace();
}
在这个示例中,我们使用 `getAssets().open()` 方法打开位于 `jsonFile/partNum.json` 相对路径的文件,并通过 `InputStream` 对象读取该文件。然后,我们将读取的数据转换为字符串格式,并最终处理该字符串中的JSON数据。
 需要注意的是,这段代码假定您已经创建了名为 `jsonFile` 的文件夹,并在其中存放了 `partNum.json` 文件。如果您没有这样做,请先创建该文件夹并将文件存放在其中,然后再使用以上代码读取文件。

Android 项目中外部文件必须放在assets文件夹里面吗,有其他替代的方法吗‘

Android项目中,您可以将外部文件放在其他位置,例如res/raw目录或磁盘上的任何位置,但是使用这些文件时需要使用不同的方法。以下是一些替代方法:
 1. Res/raw目录:将文件放在res/raw目录下,并使用以下代码读取文件:
InputStream is = getResources().openRawResource(R.raw.filename);
2. 磁盘上的任何位置:将文件放在磁盘上的任何位置,并使用以下代码读取文件:
File file = new File("/path/to/file");
InputStream is = new FileInputStream(file);
需要注意的是,在使用这些方法时,您需要获取正确的文件路径,并在读取文件时处理可能会发生的异常。此外,如果您需要在应用程序升级后更改文件,则需要考虑将文件放在外部存储设备上,并在运行时请求适当的权限来访问该设备。

解压文件(可解压包含某关键字文件)

fun unzipFileByKeyword(zipFile: File?, destDir: File?, keyword: String?): List<File>? {  
        if (zipFile == null || destDir == null) return null  
        val files: ArrayList<File> = ArrayList()  
        val zip = ZipFile(zipFile)  
        val entries: Enumeration<*> = zip.entries()  
        try {  
            if (isSpace(keyword)) {  
                while (entries.hasMoreElements()) {  
                    val entry = entries.nextElement() as ZipEntry  
                    val entryName = entry.name.replace("\\", "/")  
                    if (entryName.contains("../")) {  
                        Log.e("ZipUtils", "entryName: $entryName is dangerous!")  
                        continue  
                    }  
                    if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files  
                }  
            } else {  
                while (entries.hasMoreElements()) {  
                    val entry = entries.nextElement() as ZipEntry  
                    val entryName = entry.name.replace("\\", "/")  
                    if (entryName.contains("../")) {  
                        Log.e("ZipUtils", "entryName: $entryName is dangerous!")  
                        continue  
                    }  
                    if (entryName.contains(keyword!!)) {  
                        if (!unzipChildFile(destDir, files, zip, entry, entryName)) return files  
                    }  
                }  
            }  
        }catch (e:Exception){  
            e.printStackTrace()  
        } finally {  
            zip.close()  
        }  
        return files  
    }  
  
    @Throws(IOException::class)  
    private fun unzipChildFile(destDir: File, files: ArrayList<File>, zip: ZipFile, entry: ZipEntry, name: String): Boolean {  
        val file = File(destDir, name)  
        files.add(file)  
        if (entry.isDirectory) {  
            return createOrExistsDir(file)  
        } else {  
            if (!createOrExistsFile(file)) return false  
            var inputStream: InputStream? = null  
            var outputStream: OutputStream? = null  
            try {  
                inputStream = BufferedInputStream(zip.getInputStream(entry))  
                outputStream = BufferedOutputStream(FileOutputStream(file))  
                val buffer = ByteArray(8192)  
                var len: Int  
                while (inputStream.read(buffer).also { len = it } != -1) {  
                    outputStream.write(buffer, 0, len)  
                }  
            } finally {  
                inputStream?.close()  
                outputStream?.close()  
            }  
        }  
        return true  
    }

将以上代码进行优化后

fun unzipFileByKeyword(zipFile: File?, destDir: File?, keyword: String?): List<File>? {
    if (zipFile == null || destDir == null) return null
    if (!destDir.exists() || !destDir.isDirectory) return null
     val files: MutableList<File> = mutableListOf()
    val zip = ZipFile(zipFile)
     try {
        val entries = zip.entries().asSequence().filter { !it.isDirectory && (keyword == null || it.name.contains(keyword)) }
        entries.forEach { entry ->
            val entryName = entry.name.replace("\\", "/")
             if (entryName.contains("../")) {
                Log.e("ZipUtils", "entryName: $entryName is dangerous!")
                return@forEach
            }
             val file = File(destDir, entryName)
            if (!createOrExistsFile(file)) return@forEach
             zip.getInputStream(entry)?.use { inputStream ->
                BufferedOutputStream(FileOutputStream(file)).use { outputStream ->
                    inputStream.copyTo(outputStream)
                }
            }
             files.add(file)
        }
    } catch (e: Exception) {
        e.printStackTrace()
    } finally {
        zip.close()
    }
     return files
}
 fun createOrExistsDir(file: File): Boolean {
    return if (file.exists()) {
        file.isDirectory
    } else {
        file.mkdirs()
    }
}
 fun createOrExistsFile(file: File): Boolean {
    if (file.exists()) return file.isFile
    if (!createOrExistsDir(file.parentFile)) return false
    return try {
        file.createNewFile()
    } catch (e: IOException) {
        e.printStackTrace()
        false
    }
}

zip.entries().asSequence().filter ===>
这段代码返回一个Sequence<ZipEntry>对象,它包含.zip文件中的所有条目(entries)。具体地说,它使用.entries()方法获取所有的条目,并将它们作为Sequence对象返回。然后,使用asSequence()方法将此Sequence转换为另一个Sequence对象,以便后续操作。接下来,使用filter()方法对转换后的Sequence中的条目进行过滤。这个方法使用一个lambda表达式来指定过滤条件。在这个例子中,过滤条件是ZipEntry对象的名称(name)属性以某个特定字符串结尾。最后,返回的Sequence对象包含所有满足过滤条件的条目。
    
val entries = zip.entries().asSequence().filter { 
                !it.isDirectory && (keyword == null || it.name.contains(keyword)) 
            } ===>
这段代码的返回值是一个经过筛选的ZipEntry对象的Sequence序列。其中每个ZipEntry对象都满足以下两个条件之一:
 1.它的名称(name)属性以指定的关键字(keyword)结尾(如果指定了关键字)。
2.它不是一个目录(isDirectory属性为false)。
 这个Sequence序列中的每个对象都表示.zip文件中的一个文件或目录。

1.将返回类型更改为MutableListFile>,而不是可为nullListFile>2.添加了一个检查,以确保目标目录存在并且是一个目录。
3.使用Kotlin的asSequence()函数将Enumeration转换为Sequence4.使用use功能自动处理关闭的流和文件。
5.删除了两次检查关键字的多余if语句。
6.将用于创建文件或目录的代码提取到单独的函数中。
7.ArrayList更改为MutableList,以允许使用Kotlin的可变列表函数。
8.删除了不必要的变量声明(例如inputStream、outputStream、len)。
9.使用copyTo()函数将字节从输入流复制到输出流。
10.ArrayList更改为MutableList,以允许使用Kotlin的可变列表函数。

以上代码中keyword如何使用

这段代码中的"keyword"是一个可选的参数,用于过滤zip文件中的条目。如果传递了"keyword"参数,则只有那些名称中包含它的条目才会被提取。如果未传递"keyword"参数,则所有条目都会被提取。此处的关键字是一个字符串类型的变量,可以在函数调用时作为参数传递,例如:
val extractedFiles = unzipFileByKeyword(zipFile, destDir, "example")
这将用"example"作为关键字来提取zip文件中名称中包含"example"的条目,并将结果存储在"extractedFiles"列表中。如果您不想使用关键字,请将其设置为null,例如:
val extractedFiles = unzipFileByKeyword(zipFile, destDir, null)
这将提取zip文件中的所有条目,而不考虑名称。