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

# Groovy 文件

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

## 对 JSON 的操作

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

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

API功能
JsonSlurperJSON转化为对象
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晚」

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Groovy 内置了 `JsonSlurper` 和 `MarkupBuilder` 类可以方便地进行 JSONXML 的转换。 下面是一个将 JSON 转换为 XML 的例子: ```groovy import groovy.json.JsonSlurper import groovy.xml.MarkupBuilder def json = '{"name": "John", "age": 30, "city": "New York"}' def slurper = new JsonSlurper() def object = slurper.parseText(json) def writer = new StringWriter() def builder = new MarkupBuilder(writer) builder.'person' { 'name'(object.name) 'age'(object.age) 'city'(object.city) } def xml = writer.toString() println xml ``` 输出: ```xml <person> <name>John</name> <age>30</age> <city>New York</city> </person> ``` 其中,`JsonSlurper` 类用于解析 JSON 字符串为 Groovy 的对象,`MarkupBuilder` 类用于构建 XML 文档。在 `MarkupBuilder` 中,使用 `'person'` 定义了父节点,然后使用 `'name'`、`'age'` 和 `'city'` 分别作为子节点,使用 `object` 中的属性作为文本内容。 如果你想要将 XML 转换为 JSON,可以使用 `JsonOutput` 类的 `toJson` 方法将 Groovy 对象转换为 JSON 字符串,示例代码如下: ```groovy import groovy.json.JsonOutput import groovy.xml.XmlUtil def xml = '<person><name>John</name><age>30</age><city>New York</city></person>' def root = XmlUtil.parseText(xml) def object = [:].withDefault { k -> root."$k"*.text().join('') } def json = JsonOutput.toJson(object) println json ``` 输出: ```json {"name":"John","age":30,"city":"New York"} ``` 在这里,`XmlUtil` 类用于将 XML 字符串解析为 DOM 对象,然后使用 `withDefault` 方法将 DOM 对象转换为 Groovy 的 `Map` 对象。最后使用 `JsonOutput` 类的 `toJson` 方法将 `Map` 对象转换为 JSON 字符串。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值