scala中json4s 使用详解

预备知识

json4s的数据结构AST (Abstract Syntax Tree)。

sealed abstract class JValue
case object JNothing extends JValue // 'zero' for JValue
case object JNull extends JValue
case class JString(s: String) extends JValue
case class JDouble(num: Double) extends JValue
case class JDecimal(num: BigDecimal) extends JValue
case class JInt(num: BigInt) extends JValue
case class JBool(value: Boolean) extends JValue
case class JObject(obj: List[JField]) extends JValue
case class JArray(arr: List[JValue]) extends JValue

type JField = (String, JValue)

我们可以通过 json4s 对json所做的操作如下图所示,中间为 Json AST (简单理解就是一个用JValue表示的 JSON)。
在这里插入图片描述

另外,org.json4s下定义了很多scala原生数据转JValue的隐式转换(即多数操作下可以把原生数据当做JValue直接使用)
在这里插入图片描述
但是注意:Tuple不能自动转为JValue,在需要转换的时候,render先转一下,如:json merge render("height",175)

一、 创建json对象

{"name":"luca", "id": "1q2w3e4r5t", "age": 26, "url":"http://www.nosqlnocry.wordpress.com"}

方式1:parse函数

// parse from string example,得到的是JValue对象
var json = parse("""{"name":"luca", "id": "1q2w3e4r5t", "age": 26, "url":"http://www.nosqlnocry.wordpress.com"}""")

方式2:dsl创建

org.json4s中定义了从tuple到JValue的操作符

// DSL example,是JObject对象
var json = ("name","luca") ~ ("id","1q2w3e4r5t") ~ ("age",26) ~ ("url","http://www.nosqlnocry.wordpress.com")
// tuples can be defined also like this: ("id" -> "1q2w3e4r5t")
println(json)
JObject(List((name,JString(luca)), (id,JString(1q2w3e4r5t)), (age,JInt(26)), (url,JString(http://www.nosqlnocry.wordpress.com))))

二、常用操作

2.1 新增一个field

注意,以下两个方法都不会覆盖,若原json中已有height,则新json中会有两个height.

//法1:使用dsl,JObject才有~方法
json = json ~ ("height" -> 175) 
//法2: 使用merge,要求类型和左边一致,所以json为parse出来的JValue时,要用render生成JValue再merge
json = json merge render("height",175)

2.2 更新一个field

使用 transformField

json = json transformField {
    case JField("name", _) => ("NAME", JString("Luca")) //还可重命名
    case JField("age", JInt(age)) => ("age", JInt(age+1))//更新值
}

json = json merge render("age",26) //若json 中已有"age"

2.3 删除一个field

json = json removeField {
    case JField("NAME", _) => true //被删除
    case _ => false
}
// 或等价的
json = json filterField {
    case JField("NAME", _) => false 
    case _ => true //被保留
    }

2.4 获取一个field

println(compact(json \\ "age")) // 27 嵌套获取-见下
println(compact(json \ "age"))  // 27
println(compact(json.children(1))) // 27

三、高阶操作

{
	"name": "luca",
	"id": "1q2w3e4r5t",
	"age": 26,
	"url": "http://www.nosqlnocry.wordpress.com",
	"url": "https://nosqlnocry.wordpress.com",
	"loginTimeStamps": [
		1434904257,
		1400689856,
		1396629056
	],
	"messages": [
		{
			"id": 1,
			"content": "Please like this post!"
		},
		{
			"id": 2,
			"content": "Forza Roma!"
		}
	],
	"profile": {
		"id": "my-nickname",
		"score": 123,
		"avatar": "path.jpg"
	}
}

3.1 选取field

println(JSON)
//JObject(List((name,JString(luca)), (id,JString(1q2w3e4r5t)), (age,JInt(26)), (url,JString(http://www.nosqlnocry.wordpress.com)), (url,JString(https://nosqlnocry.wordpress.com)), (loginTimeStamps,JArray(List(JInt(1434904257), JInt(1400689856), JInt(1396629056)))), (messages,JArray(List(JObject(List((id,JInt(1)), (content,JString(Please like this post!)))), JObject(List((id,JInt(2)), (content,JString(Forza Roma!))))))), (profile,JObject(List((id,JString(my-nickname)), (score,JInt(123)), (avatar,JString(path.jpg)))))))


println(JSON\\"id") //获取所有嵌套的id数据
// prints: JObject(List((id,JString(1q2w3e4r5t)), (id,JInt(1)), (id,JInt(2)), ...

println(JSON\"id")//获取第一层的id数据
// prints: JString(1q2w3e4r5t)

println(JSON\"url") //如果第一层有多个,则返回JArray
// prints: JArray(List(JString(http://www...), JString(https://nosqlnocry...

val messagesIds = (JSON \ "messages") \ "id" //获取JAray中的id数据
println(messagesIds)
// prints: JArray(List(JInt(1), JInt(2)))
println(messagesIds.values)
// prints: List(1,2)
//或用for语句
val messagesIds2=  for {
      JObject(child) <- JSON
      JField("id", JInt(id)) <- child
    } yield id
 println(messagesIds2)
 // prints: List(1,2)

for语句的<-在JValue中做了特殊处理,会返回所有匹配项。

    for {
      JObject(child) <- JSON //这回匹配所有JObject,不管是不是嵌套
    }{
      println(child)
    }
//List((name,JString(luca)), (id,JString(1q2w3e4r5t)), (age,JInt(26)), (url,JString(http://www.nosqlnocry.wordpress.com)), (url,JString(https://nosqlnocry.wordpress.com)), (loginTimeStamps,JArray(List(JInt(1434904257), JInt(1400689856), JInt(1396629056)))), (messages,JArray(List(JObject(List((id,JInt(1)), (content,JString(Please like this post!)))), JObject(List((id,JInt(2)), (content,JString(Forza Roma!))))))), (profile,JObject(List((id,JString(my-nickname)), (score,JInt(123)), (avatar,JString(path.jpg))))))
//List((id,JInt(1)), (content,JString(Please like this post!)))
//List((id,JInt(2)), (content,JString(Forza Roma!)))
//List((id,JString(my-nickname)), (score,JInt(123)), (avatar,JString(path.jpg)))

JValue的<-调用的是

    def foreach(f: JValue => Unit): Unit =
      self.filter(p).foreach(f)

Array的<-调用的是

  def foreach[U](f: A => U): Unit = {
    var i = 0
    val len = length
    while (i < len) { f(this(i)); i += 1 }
  }

3.2 取出field

println(compact(render(JSON \ "messages")))
// prints: [{"id":1,"content":"Please like this post!"},{"id":2,"content":"Forza Roma!"}]
println(pretty(render((JSON \ "messages")\"content")))
// prints: [ "Please like this post!", "Forza Roma!" ] // note it is not compacted anymore

println(pretty(render(JSON \ "age")))
// prints: 26

println(compact(render(JSON \ "name")))
// prints: "luca" // note the apostrophes

var name = for { JString(x) <- (JSON \\ "name") } yield x //或用for表达式去掉双引号
println(name(0))
// prints: luca

var name = (JSON \ "name") //或用values去掉双引号,保留对应基本类型时,推荐这种方法
println(name.values)
// prints: luca

implicit val formats = DefaultFormats
val name = (JSON \ "name").extract[String]//或直接extract,已知需要的类型时推荐这种方法
println(name)
// prints: luca

name.values 原理:在这里插入图片描述

3.3 查找和过滤filed

//返回第一个遇到的元素
val URL = JSON findField {
case JField("url", _) => true
case _ => false
}
println(URL)
// prints: Some((url,JString(http://www.nosqlnocry.wordpress.com)))

// 返回所有符合条件的元素
val URLs = JSON filterField {
case JField("url", _) => true
case _ => false
}
println(URLs)
// prints: List((url,JString(http://www.nosqlnocry...)), (url,JString(https://nosqlnocry...)

3.4 合并与差异另一个Json2:merge和diff

{
	"messages": [
		{
			"id": 3,
			"content": "how to merge?"
		}
	],
	"url": "anotherURL",
	"loginTimeStamps": 1400689856,
	"profile": {
		"avatar": "new.jpg"
	},
	"new": "new value"
}

Json1 merge Json2

  1. 如果字段法Json1/f1与Json2/f1结构不同,或者仅具有简单结构,则Json2会替换Json1的f1
  2. 若结构相同且为复杂结构,则会合并
  3. 若Json2/f1在Json1中不存在,则新增
    在这里插入图片描述
    diff 获取两个JSon间的不同(用得少):
val newUserJSON = """
 {
   "name":"luca",
   "id": "anotherID",
   "age": 26,
   "url":"http://www.nosqlnocry.wordpress.com",               
   "profile":{"id":"another-nickname", "score":99999, "avatar":"path.jpg"}
}
"""    
val Diff(changed, added, deleted) = JSON diff parse(newUserJSON)

println(compact(render(changed)))
println(added)
println(pretty(render(deleted)))
/* print:
{"id":"anotherID","profile":{"id":"another-nickname","score":99999}}
JNothing
{
  "url" : "https://nosqlnocry.wordpress.com",
  "loginTimeStamps" : [ 1434904257, 1400689856, 1396629056 ],
  "messages" : [ {
    "id" : 1,
    "content" : "Please like this post!"
  }, {
    "id" : 2,
    "content" : "Forza Roma!"
  } ]
}*/

3.5 类和JSon间的转换:decompose, extract, write和read

case class Item(info: String, rank: Int)
case class Item2(info: String, rank: Int, name:String)
implicit val formats: Formats = DefaultFormats

val vMap=Map("info" -> "abcd", "rank" -> 123, "other" -> "dsf")
val jsonStr = write(vMap)
println(jsonStr)
//{"info":"abcd","rank":123,"other":"dsf"}

val json = parse(jsonStr)
println(json)
 val json2 = Extraction.decompose(vMap)//可以理解为等价于parse(write(vMap))
println(json2)


val json=parse(jsonStr)
//val json2=
println(json.extract[Map[String,Any]])
//Map(info -> abcd, rank -> 123, other -> dsf)
println(read[Map[String,Any]](jsonStr))//可理解为和json.extract效果一样,但是跳过了将str转为JValue对象的过程
//Map(info -> abcd, rank -> 123, other -> dsf)


println(json.extract[Item])//case class 的字段名要和json的field一致,可少不可多与json有的field
//Item(abcd,123)
println(read[Item](jsonStr))
//Item(abcd,123)

println(json.extract[Item2])//不可多于json有的field
//报错,org.json4s.MappingException: No usable value for name

println(read[Item2](jsonStr))
//报错,org.json4s.MappingException: No usable value for name

不用默认格式:(非scala基类作为父类的话,默认格式解析会出错)

  trait Animal
  case class Dog(name: String) extends Animal
  case class Fish(weight: Double) extends Animal
  case class Animals(animals: List[Animal])


    implicit val formats1: Formats = DefaultFormats
    val formats2: Formats = Serialization.formats(ShortTypeHints(List(classOf[Dog], classOf[Fish])))
    implicit val mf = manifest[Animals]

    val ser1 = write(Animals(Dog("pluto") :: Fish(1.2) :: Nil))(formats1)
    val ser2 = write(Animals(Dog("pluto") :: Fish(1.2) :: Nil))(formats2)

    println(ser1)
    //{"animals":[{"name":"pluto"},{"weight":1.2}]}
    println(ser2)
    //{"animals":[{"jsonClass":"BasicTest$Dog","name":"pluto"},{"jsonClass":"BasicTest$Fish","weight":1.2}]}



    println(read[Animals](ser2)(formats2, mf))
    //Animals(List(Dog(pluto), Fish(1.2)))
    println(parse(ser2).extract[Animals](formats2,mf))
    //Animals(List(Dog(pluto), Fish(1.2)))
    println( read[Animals](ser2)(formats1,mf))// 报错
    //org.json4s.MappingException: No usable value for animals,No constructor for type Animal, JObject(List((jsonClass,JString(BasicTest$Dog)), (name,JString(pluto))))


    println( read[Animals](ser1))//等价于println( read[Animals](ser1)(formats1,mf)) ,报错
    //org.json4s.MappingException: No usable value for animals No constructor for type Animal, JObject(List((name,JString(pluto))))
    println(parse(ser1).extract[Animals])//报错
    //org.json4s.MappingException: No usable value for animals No constructor for type Animal, JObject(List((name,JString(pluto))))
    println(parse(ser2).extract[Animals])//报错
    //org.json4s.MappingException: No constructor for type Animal, JObject(List((jsonClass,JString(BasicTest$Dog)), (name,JString(pluto))))

参考

官方教程
WORKING WITH JSON IN SCALA USING THE JSON4S LIBRARY (PART ONE)
WORKING WITH JSON IN SCALA USING THE JSON4S LIBRARY (PART TWO)
Purpose of render in json4s

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/84761.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

C++实现字符串的逆置

目录 C和C的区别 【1】C对C的扩充 【2】C对C的兼容 第一个C程序 【1】hello world 【2】cout标准输出流对象 i&#xff09;介绍 ii&#xff09;运算 iii&#xff09;cout的使用 iv&#xff09;使用cout指定格式的输出 练习&#xff1a;1、输出斐波那契的前10项。 【3】…

【Java从0到1学习】10 Java常用类汇总

1. System类 System类对读者来说并不陌生&#xff0c;因为在之前所学知识中&#xff0c;需要打印结果时&#xff0c;使用的都是“System.out.println();”语句&#xff0c;这句代码中就使用了System类。System类定义了一些与系统相关的属性和方法&#xff0c;它所提供的属性和…

使用 Feature Flags 与可观测工具实现数据库灰度迁移

场景描述 很多企业会遇到数据库升级、或数据库迁移的情况&#xff0c;尤其是在自建数据库服务向云数据库服务、自建机房向云机房、旧数据库向新数据库迁移等场景。 然而&#xff0c;我们需要在整个移植过程中保证其稳定性、避免数据遗失、服务宕机等情况&#xff0c;最常见的移…

将eNSP Pro部署在华为云是什么体验

eNSP Pro简介 eNSP Pro 是华为公司数据通信产品线新推出的数通设备模拟器&#xff0c;主要应用在数据通信技能培训&#xff0c;为使用者提供华为数据通信产品设备命令行学习环境。 具备的能力 多产品模拟能力&#xff1a;支持数据通信产品线NE路由器、CE交换机、S交换机、AR…

实验二 Hdoop2.7.6+JDK1.8+SSH的安装部署与基本操作

系列文章目录 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 提示&#xff1a;写完文章后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目…

阿里云CDN加速器基本概念与购买开通

文章目录 1.CDN加速器的基本概念1.1.CDN加速器基本介绍1.2.网站引入CDN加速器的架构图1.3.CDN加速器的工作原理1.4.引入CDN后域名解析变成了CNAME&#xff1f; 2.开通阿里云CDN加速服务 1.CDN加速器的基本概念 CDN加速器官方文档&#xff1a;https://help.aliyun.com/product/…

Spring Clould 部署 - Docker

视频地址&#xff1a;微服务&#xff08;SpringCloudRabbitMQDockerRedis搜索分布式&#xff09; 初识Docker-什么是Docker&#xff08;P42&#xff0c;P43&#xff09; 微服务虽然具备各种各样的优势&#xff0c;但服务的拆分通用给部署带来了很大的麻烦。 分布式系统中&…

Redis Lua脚本执行原理和语法示例

Redis Lua脚本语法示例 文章目录 Redis Lua脚本语法示例0. 前言参考资料 1. Redis 执行Lua脚本原理1.1. 对Redis源码中嵌入Lua解释器的简要解析&#xff1a;1.2. Redis Lua 脚本缓存机制 2. Redis Lua脚本示例1.1. 场景示例1. 请求限流2. 原子性地从一个list移动元素到另一个li…

计算机网络第2章(物理层)

计算机网络第2章&#xff08;物理层&#xff09; 2.1 物理层的基本概念2.2 物理层下面的传输媒体2.2.1 导引型传输媒体2.2.2 非导引型传输媒体 2.3 传输方式2.3.1 串行传输和并行传输2.3.2 同步传输和异步传输2.3.3 单向通信&#xff08;单工&#xff09;、双向交替通信&#x…

PyCharm PyQt5 开发环境搭建

环境 python&#xff1a;3.6.x PyCharm&#xff1a;PyCharm 2019.3.5 (Community Edition) 安装PyQT5 pip install PyQt5 -i https://pypi.douban.com/simplepip install PyQt5-tools -i https://pypi.douban.com/simple配置PyCharm PyQtUIC Program &#xff1a;D:\Pytho…

sql数据导出到excel

一、打开Navicat Premium 12 二、导出

SpringBoot 配置优先级

一般而言&#xff0c;SpringBoot支持配置文件进行配置&#xff0c;即在resources下的application.properties或application.yml。 关于配置优先级而言&#xff0c; application.properties>application.yml>application.yaml 另外JAVA程序程序还支持java系统配置和命令行…

Python-主线程控制子线程结束-2

需求&#xff1a;主线程创建工作子线程和键盘输入监听子线程。 当用户输入 0 后&#xff0c; 工作子线程会收到停止信号并退出&#xff0c;此时键盘输入监听线程仍然运行&#xff1b; 当用户输入 1 后&#xff0c;会建立新的工作子线程&#xff1b; …

计网第三章(数据链路层)(五)

目录 一、以太网交换机自学习和转发帧的过程 1.两层交换机和三层交换机 2.以太网交换机的基本原理 3.具体实现过程 一、以太网交换机自学习和转发帧的过程 1.两层交换机和三层交换机 大家可能注意到平常做题时有叫两层交换机&#xff0c;或者三层交换机的。 两层交换机就…

html表格中加入斜线,使用css给table表格表头单元格添加斜线

背景&#xff1a;业务给了90张word电子表格&#xff0c;需要用html设计出来。 如图所示&#xff0c;红色区域的下斜线如何实现&#xff1f; 先说结论&#xff1a;html中table没有直接的斜线表头标签&#xff0c;但结合css、svg之类的可以实现。 #lineTd{ background:#FFFFFF u…

UG\NX二次开发 创建中心线

文章作者:里海 来源网站:王牌飞行员_里海_里海NX二次开发3000例,C\C++,Qt-CSDN博客 简介: 下面是在制图模块创建中心线的例子,用的是ufun函数。 效果: 代码: #include "me.hpp"#include <stdio.h> #include <string.h> #include <uf.h>…

Qt关于hex转double,或者QByteArray转double

正常的00 ae 02 33这种类型的hex数据类型可以直接通过以下代码进行转换 double QDataConversion::hexToDouble(QByteArray p_buf) {double retValue 0;if(p_buf.size()>4){QString str1 byteArrayToHexStr(p_buf.mid(0,1));QString str2 byteArrayToHexStr(p_buf.mid(1,…

如何使用CSS实现一个响应式视频播放器?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 使用CSS实现响应式视频播放器⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击上方或者右侧链接订阅本专栏哦 几何带你启航前端之旅 欢迎来到前端入门之旅&#xff01;这个专栏是为那些对Web开发感兴趣…

数字孪生流域共建共享相关政策解读

当前数字孪生技术在水利方面的应用刚起步&#xff0c;2021年水利部首次提出“数字孪生流域”概念&#xff0c;即以物理流域为单元、时空数据为底座、数学模型为核心、水利知识为驱动&#xff0c;对物理流域全要素和水利治理管理活动全过程的数字映射、智能模拟、前瞻预演&#…

志凌海纳 SmartX 携手灵雀云推出全栈云原生联合解决方案

近日&#xff0c;北京志凌海纳科技有限公司&#xff08;以下简称“SmartX”&#xff09;与北京凌云雀科技有限公司&#xff08;以下简称“灵雀云”&#xff09;联合推出全栈云原生联合解决方案&#xff0c;为客户提供从基础设施到容器云平台的一站式服务&#xff0c;加速客户云…