美文网首页Gradle构建
Gradle-Groovy 对 XML,JSON 以及文件的操作

Gradle-Groovy 对 XML,JSON 以及文件的操作

作者: 未见哥哥 | 来源:发表于2018-07-08 23:16 被阅读4次

    # Groovy 文件

    • 对 JSON 的操作;
    • 对 XML 的操作;
    • 对普通文件的操作;
    • 总结;

    ## 对 JSON 的操作

    JSON 的操作在 Android 开发中是非常之常用的,客户端请求服务端接口返回的数据类型一般就是 JSON 格式的数据。

    下面看看 Groovy 对 JSON 有什么好的扩展。

    API 功能
    JsonSlurper JSON转化为对象
    JsonOutput 对象转化为JSON

    ### JSON转化为对象

    在 Groovy 中提供 JsonSlurper(JsonParser的API是一样的) 将字符串解析为对象。

    • 定义一个 JsonSlurper 对象
    def jsonSlurper = new JsonSlurper()
    
    • 解析 json 字符串
    def object = jsonSlurper.parseText('''
        {
            "name":"六号表哥",
            "age":26,
            "level":null,
            "isMale":true
        }
    ''')
    
    • 访问解析后的值
    println object.getClass()//class org.apache.groovy.json.internal.LazyMap
    println object.name//六号表哥"
    println object.age//26
    

    上面的例子中,我们做了一下几件事

    • 创建一个 JsonSlurper 对象
    • 使用 JsonSlurper 对象来解析 json 字符串
    • 通过 key 来访问解析后的值

    在 Groovy 中可以解析的数据类型分别有:

    • 基本数据类型
    • 字符串类型
    • map 类型
    • list 类型
    • null

    具体什么意思呢?也就是说,json 的 value 可以是以上这么多种数据类型。

    注意:这里有一个很蛋疼的问题,我们在使用 Gson 进行 json 转化为 bean 时,如果 json 字符串中多了一个 bean 没有定义的字段,那么 gson 在解析时就会忽略这个字段,而 JsonSluper 并不会忽略,它会在解析就直接抛出异常,我目前还不知道怎么解决这个问题,如果在解析时,连这个功能都没有,那么我也不会去使用 JsonSlurper 。

    下面来演示一下这个问题:

    • 定义一个 Person 类,只有两个属性,分别为 name 和 age。
    class Person implements Serializable{
    
        String name
        int age
    
        String toString() {
            "${name} is $age year old"
        }
    }
    
    • 使用 JsonSlurper 将其转化为 Person 对象

    在这里因为没有isMale这个属性,因此在运行时就抛出异常了。

    Person person = jsonSlurper.parseText('''
        {
            "name":"六号表哥",
            "age":26,
            "isMale":true
        }
    ''')
    
    //异常:
    org.codehaus.groovy.runtime.metaclass.MissingPropertyExceptionNoStack:
    No such property: isMale for class: Person
    

    也许新的语言特性不再需要像 Java 一样定义实体类,通过 JsonSlurper 解析完成之后就是一个 LazyMap 对象,直接调用属性获取数据即可, JS 语言也是这样的。

    ### 对象转化为JSON

    在 Groovy 中提供 JsonOutput 将对象解析为 json 字符串。

    Person person = new Person("六号表哥", age: 26)
    def json = JsonOutput.toJson(person)
    //JsonOutput.prettyPrint 输出带有 json 格式
    println JsonOutput.prettyPrint(json)
    

    ## 对 XML 的操作

    Groovy 支持解析 XML 和生成 XML 的功能。

    API 功能
    groovy.util.XmlParser 解析 xml
    groovy.util.XmlSlurper 解析 xml
    groovy.xml.MarkupBuilder 生成 xml

    ### XmlSlurper 解析 XML

    • 定一个 xml 字符串
    String books = '''
        <response version-api="2.0">
            <value>
                <books>
                    <book available="20" id="1">
                        <title>Don Xijote</title>
                        <author id="1">Manuel De Cervantes</author>
                    </book>
                    <book available="14" id="2">
                        <title>Catcher in the Rye</title>
                       <author id="2">JD Salinger</author>
                   </book>
                   <book available="13" id="3">
                       <title>Alice in Wonderland</title>
                       <author id="3">Lewis Carroll</author>
                   </book>
                   <book available="5" id="4">
                       <title>Don Xijote</title>
                       <author id="4">Manuel De Cervantes</author>
                   </book>
               </books>
           </value>
        </response>
    '''
    
    • XmlSlurper 对象的创建
    XmlSlurper xmlSlurper = new XmlSlurper()
    
    • 解析 xml 字符串
    def response = xmlSlurper.parseText(books);
    

    ### 获取标签内容和属性

    #### 获取标签的内容

    text() 
    
    assert response.value.books.book[0].title.text() == 'Don Xijote'
    

    #### 获取标签的属性

    获取标签的属性有以下三种方式:

    • a["@href"]
    println response.value.books.book[0].author["@id"]
    
    • a.'@href'
    println response.value.books.book[0].author."@id" 
    
    • a.@href
    println response.value.books.book[0].author.@id 
    

    ### 遍历 XML

    • 广度遍历 book 下的 title 子标签的内容
    response.value.books.book.each {
        book ->
            println  book.title.text()
    }
    
    • 深度遍历 book 下的 title 子标签的内容
    //深度遍历
    response.depthFirst().findAll {
        node ->
            return (node.name().equals("book"))
    }.collect {
        node -> println node.title.text()
    }
    

    ### 生成 XML

    在 Groovy 提供了MarkupBuilder 来动态生成 XML 内容。
    以下通过一个实际的例子来生成一段 xml 内容

    生成 XML
    • 定义 Writer

    这里定义一个 StringWriter ,它是 Writer 的子类,MarkupBuilder 生成 xml 会写入到 StringWriter 中。

    def sw = new StringWriter();
    
    • 闯将 MarkupBuilder
    MarkupBuilder builder = new MarkupBuilder(sw);
    
    • 开始写 xml 内容

    注意:uses-permission 像这种标签名字就需要使用'uses-permission'来表示,不然编译是失败的,因为有-特殊符号,如果就只有一个单词,那么就需要用 '' 来表示。

    builder.manifest(package: "com.example.app", xmlns: 'android="http://schemas.android.com/apk/res/android"') {
        'uses-permission'('android:name': '"android.permission.INTERNET"')
        application('android:name': "com.example.AppApplication") {
            activity('android:name': '"com.example.app.activity.SplashActivity"') {
                'intent-filter' {
                    action('android:name': '"android.intent.action.MAIN"')
                    category('android:name': '"android.intent.category.LAUNCHER"')
                }
            }
        }
    }
    

    ## 普通文件

    • 在 Groovy 中对文件的操作跟 Java 是完全兼容的,并且 Groovy 也提供更加简洁的操作方式。
    • 对于文件的操作不外乎就是对文件的读和写。

    ### 定义一个文件对象

    在 Groovy 中定义文件的方式跟 Java 是一致的。下面指定的文件是工程下某一个文件为例。

    def file = new File("../GroovyWorkspace.iml")
    
    定义一个文件对象

    ### 读取文件

    Groovy 提供了快速读取文件内容的 api ,分别如下所示:

    • getText() 一次性读取所有的内容到一个字符串中
    • readLines() 一次性读取所有的内容到集合中
    • eachLine() 逐行读取内容

    一次性读取文件到内存中,getText()返回一个字符串,该字符串就表示文件的内容。

    /**
     * Read the content of the File and returns it as a String.
     *
     * @param file the file whose content we want to read
     * @return a String containing the content of the file
     * @throws IOException if an IOException occurs.
     * @since 1.0
     */
    public static String getText(File file) throws IOException {
        return IOGroovyMethods.getText(newReader(file));
    }
    

    实际调用很简单:

    def result =  file.getText()
    println result
    

    readLines() 返回一个集合,该集合的元素对应于文件中每一个行内容。

    /**
     * Reads the file into a list of Strings, with one item for each line.
     *
     * @param file a File
     * @return a List of lines
     * @throws IOException if an IOException occurs.
     * @see IOGroovyMethods#readLines(java.io.Reader)
     * @since 1.0
     */
    public static List<String> readLines(File file) throws IOException {
        return IOGroovyMethods.readLines(newReader(file));
    }
    

    实际调用很简单:

    //先获取集合,然后遍历集合内容
    file.readLines().each{
        line->
            println line
    }
    

    eachLine方法进行逐行获取,传入的闭包的参数1表示遍历当前行的内容,参数2是可选参数,表示当前行的行号。

    /**
     * Iterates through this file line by line.  Each line is passed to the
     * given 1 or 2 arg closure.  The file is read using a reader which
     * is closed before this method returns.
     *
     * @param self    a File
     * @param closure a closure (arg 1 is line, optional arg 2 is line number starting at line 1)
     * @return the last value returned by the closure
     * @throws IOException if an IOException occurs.
     * @see #eachLine(java.io.File, int, groovy.lang.Closure)
     * @since 1.5.5
     */
    public static <T> T eachLine(File self, @ClosureParams(value=FromString.class,options={"String","String,Integer"}) Closure<T> closure) throws IOException {
        return eachLine(self, 1, closure);
    }
    

    实际调用很简单:

    //一个参数的闭包,表示当前行的内容
    file.eachLine {
        line->
            println line
    }
    
    //两个参数的闭包,参数1表示当前行的内容,参数2表示当前行的行号
    file.eachLine {
        line,lineNum->
            println "lineNum:${lineNum} : ${line}"
    }
    

    Groovy 中可以通过 file.withReader 的方式快速的拿到 file 对应的 Reader 对象,然后进行读操作。这个操作就只有一行代码,我们来对比一下在 java 中,获取一个 file 对应的 Reader 对象的获取。

    • groovy 版本
    file.withReader {
        //这个 reader 就是 Java 中的 LineNumberReader 对象
        reader ->
            reader.readLines().each {
                line ->
                    println line
            }
    }
    
    • Java 版
    File javaFile = new File("../GroovyWorkspace.iml")
    
    LineNumberReader lineNumberReader = new LineNumberReader(new FileReader(javaFile))
    

    从上面的操作可以看出, Groovy 是一行代码就可以实现跟 Java 一样的功能,并且通过 withReader 的方式,Groovy 还帮你对流进行 close 操作,这在 java 是需要手动调用的,因此 Groovy 还是很方便的。下面的代码就是截取 withReader 的源码,在注释中已经说的很清楚,使用该方法能保证流被关闭。

    /**
     * Create a new BufferedReader for this file and then
     * passes it into the closure, ensuring the reader is closed after the
     * closure returns.
     *
     * @param file    a file object
     * @param closure a closure
     * @return the value returned by the closure
     * @throws IOException if an IOException occurs.
     * @since 1.5.2
     */
    public static <T> T withReader(File file, @ClosureParams(value=SimpleType.class, options="java.io.BufferedReader") Closure<T> closure) throws IOException {
        return IOGroovyMethods.withReader(newReader(file), closure);
    }
    
    

    ### 写入文件

    • writeText(String) 写入一个字符串到文件中
    • withWriter(closure)获取一个 Writer
    • withWriterAppend(closure)获取一个 Writer,与 withWriter 不同之处,它会以追加的方式将内容添加到文件中
    def writeFile = new File("Test.txt");
    
    if(!writeFile.exists()){
        writeFile.createNewFile()
    }
    //一句话就可以实现写入操作,不需要关心流,内部会自动关闭
    writeFile.write("hello Groovy\n");
    
    //会覆盖原有的内容
    writeFile.withWriter {
        writer->
            writer.write("hello Java\n")
    }
    
    //以追加的方式将内容添加到文件中
    writeFile.withWriterAppend { writer->
        writer.write("hello Gradle\n")
    }
    

    ### 实战:文件拷贝

    上面列举了文件的读和写的方式,下面结合读写相关 api 来实现一个文件拷贝的功能。

    /**
    * src 表示源文件路径
    * dest 表示目标文件路径
    */
    boolean copy(String src, String dest) {
        try{
            //创建一个源文件对应的 File 对象
            File srcFile = new File(src)
            
            //源文件不存在
            if(!srcFile.exists()){
                return false
            }
            
            //创建一个目标文件对应的 File 对象
            File destFile = new File(dest)
            
            //检测目标文件是否存在
            if (!destFile.exists()) {
                destFile.createNewFile()
            }
            //获取源文件的内容
            String srcText = srcFile.getText()
            //获取目标文件对应的 Writer 对象,将 srcText 写入。
            destFile.withWriter {
                writer ->
                    writer.write(srcText)
            }
            return true;
        }catch(Exception e){
            return false
        }
    }
    

    ### 实战:对象的序列化与反序化

    我们在实际开发中,经常要将一个对象序列化到本地,在 Java 中一般使用ObjectOutputStream来实现。接下来看一下 Groovy 怎么实现这功能的。

    Person person = new Person(name: "六号表哥", age: 26)
    File objFile = new File("../person")
    
    if (!objFile.exists()) {
        objFile.createNewFile()
    }
    
    //写入操作
    //通过 withObjectOutputStream 就可以快速的获取一个 ObjectOutputStream 实例对象
    objFile.withObjectOutputStream {
        out->
            out.writeObject(person)
    }
    
    //读取
    objFile.withObjectInputStream {
        inputStream ->
            Person p = inputStream.readObject()
            println "name:${p.name},age:${p.age}"//name:六号表哥,age:26
    }
    

    ## 总结

    以上总结的 Groovy 中 json ,xml 以及文件的相关操作,本文只是简单的总结了其比较常用的 api 示例,更多内容,你可以通过查阅官网来学习更多关于 groovy 中关于这三者的语法。本文中肯定有很多不足之处,有待慢慢完善,谢谢阅读。

    多思考,多总结,多实践。
    「记录于2018-07-08晚」

    相关文章

      网友评论

        本文标题:Gradle-Groovy 对 XML,JSON 以及文件的操作

        本文链接:https://www.haomeiwen.com/subject/qbnhuftx.html