scala中json4s 使用详解

news2025/1/23 17:32:00

预备知识

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

企业需求如何精准对接科研院校科技成果?

一系列科技成果转化相关政策、法律法规的出台&#xff0c;正在破除高校科技成果转化机制上的桎梏。企业科技创新的需求也越来越大&#xff0c;也充分意识到与高校合作对于推动企业发展的重要性&#xff0c;对接科技成果的热情高涨&#xff0c;合作越来越频繁。但是也存在不成功…

SpringBoot 的 RedisTemplate、Redisson

一、Jedis、Lettuce、Redisson的简介 优先使用Lettuce&#xff0c; 需要分布式锁&#xff0c;分布式集合等分布式的高级特性&#xff0c;添加Redisson结合使用。 对于高并发&#xff0c;1000/s的并发&#xff0c;数据库可能由行锁变成表锁&#xff0c;性能下降会厉害。 1.1、…

linux中ubuntu安装hashcat方法以及使用GPU破解

一、linux安装hashcat git clone https://github.com/hashcat/hashcat.git make make install二、安装驱动 驱动版本安装大全&#xff1a;https://developer.nvidia.com/cuda-toolkit-archive 查看自己显卡选择对应的版本&#xff0c;根据下面命令无脑安装就行了 wget https:/…

誉天华为数通Datacom HCIE认证课程

作为当前网络领域最具含金量的认证之一&#xff0c;华为Datacom HCIE认证不仅是网络工程师们技术能力的证明&#xff0c;也是未来行业趋势的风向标。 尽管经历了几度版本更迭、考题变化&#xff0c;但是誉天一直紧跟技术发展趋势&#xff0c;在专业领域和教学内容上始终与当前的…

生成国密密钥对

在线生成国密密钥对 生成的密钥对要妥善保管&#xff0c;丢失是无法找回的。

奇迹MU服务端IGC架设流程

奇迹MU服务端IGC架设流程 大家好我是艾西&#xff0c;今天跟大家简单聊下奇迹IGC服务端。IGC端是国外的东西全英文大家感兴趣的话也可以自己研究研究&#xff0c;整体的东西还是非常完整。扩展以及端的稳定性还是非常不错的就是有点费脑子........&#xff08;此教程为个人娱乐…

WebStorm运行vue项目

WebStorm运行vue项目&#xff08;vue2&#xff09; 1.安装webstorm 2. 需要安装node.js 环境&#xff0c;可以去官网下载 https://nodejs.org/en 3. 安装完需要查看 按winr 输入cmd进入 输入命令node -v 和npm -v 查看&#xff0c;会出现相应的版本号,代表安装成功 vue官网安…

IC设计中主要的EDA工具有哪些? (内附EDA虚拟机安装资源)

EDA工具的使用涵盖了芯片的功能设计、综合、验证、物理设计等环节&#xff0c;更是被称作“芯片设计的工作母机”。下面就来为大家具体介绍一下常见的EDA工具。&#xff08;需要EDA虚拟机安装资源文末可领取~&#xff09; 什么是EDA&#xff1f; EDA是电子设计自动化&#xf…

对象存储服务-MinIO基本集成

是什么 MinIO 是一个高性能的分布式对象存储服务&#xff0c;适合存储非结构化数据&#xff0c;如图片&#xff0c;音频&#xff0c;视频&#xff0c;日志等。对象文件最大可以达到5TB。 安装启动 mkdir -p /usr/local/minio cd /usr/local/minio# 下载安装包 wget https:/…

NextJs - Error Handling (错误处理)

Error.js 的作用 使用 error.js 文件规则&#xff0c;可以优雅地处理嵌套路由中的意外运行时错误。 在 React Error Boundary 中自动封装路由段及其嵌套子段。 使用文件系统层次结构来调整粒度&#xff0c;为特定网段量身定制错误 UI。 将错误隔离到受影响的网段&#xff0…

红帆OA SQL注入漏洞复现

0x01 产品简介 红帆iOffice.net从最早满足医院行政办公需求&#xff08;传统OA&#xff09;&#xff0c;到目前融合了卫生主管部门的管理规范和众多行业特色应用&#xff0c;是目前唯一定位于解决医院综合业务管理的软件&#xff0c;是最符合医院行业特点的医院综合业务管理平…

怎么修改图片的分辨率?

怎么修改图片的分辨率&#xff1f;很多人还不知道分辨率是什么意思&#xff0c;以为代表了图片的清晰度&#xff0c;然而并不是这样的&#xff0c;其实图片的分辨率就是图片尺寸大小的意思。修改图片的分辨率即改变图片的尺寸&#xff0c;通常以像素为单位表示。分辨率决定了图…

【腾讯云Cloud Studio实战训练营】使用Cloud Studio社区版快速构建React完成点餐H5页面还原

陈老老老板&#x1f9b8; &#x1f468;‍&#x1f4bb;本文专栏&#xff1a;生活&#xff08;主要讲一下自己生活相关的内容&#xff09; &#x1f468;‍&#x1f4bb;本文简述&#xff1a;生活就像海洋,只有意志坚强的人,才能到达彼岸。 &#x1f468;‍&#x1f4bb;上一篇…

如何优雅做好项目管理?

导言 项目本身无好坏之分&#xff0c;项目管理有做好与做坏之别。在互联网大厂的体制下&#xff0c;想要做坏一个项目很难&#xff08;可以通过换人、追加资源等方式消除风险&#xff09;&#xff0c;想要做好一个项目不容易&#xff0c;需要团队及PM付出大量心血和精力。在这…

实时业务需求和数据处理要求的满足:流处理的能力

随着数据量和数据种类的不断增加&#xff0c;企业和组织对于实时数据处理和业务响应的需求也越来越迫切。传统的批处理方式无法满足业务对于实时性和即时响应的要求&#xff0c;因此&#xff0c;流处理技术应运而生。通过流处理能力&#xff0c;可以有效地处理实时数据&#xf…

Python爬虫常用:谷歌浏览器驱动——Chromedriver 插件安装教程

前言 大家早好、午好、晚好吖 ❤ ~欢迎光临本文章 我们在做爬虫的时候经常要使用谷歌浏览器驱动&#xff0c;今天分享下这个Chromedriver 插件的安装方法。 话不多说&#xff0c;直接开搞&#xff0c;如果有什么疑惑/资料需要的可以点击文章末尾名片领取源码 第一步、打开谷…

VR仿真实训系统编辑平台赋予老师更多自由和灵活性

为了降低院校教师在VR虚拟现实方面应用的门槛&#xff0c;VR公司深圳华锐视点融合多年的VR虚拟仿真实训系统制作经验&#xff0c;制作了VR动物课件编辑器&#xff0c;正在逐渐受到师生们的关注和应用。 简单来说&#xff0c;VR畜牧专业课件编辑器是一种可以制作虚拟现实动物教学…

【LeetCode】696 . 计数二进制子串

696 . 计数二进制子串&#xff08;简单&#xff09; 思路 我们可以将字符串按照 0 和 1 的连续段分组&#xff0c;存在 count 数组中。例如 00111011 可以得到&#xff1a;count {2,3,1,2}。这里 count 数组中两个相邻的数一定代表的是两种不同的字符。假设 count 数组中两个相…

vue3生成点选验证码,前端校验

先看效果图 verify.vue&#xff1a;源码{用时一天半的破轮子}&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; <template><div class"outside" ref&q…

寻路算法小游戏

寻路算法小demo 寻路算法有两种&#xff0c;一种是dfs 深度优先算法&#xff0c;一种是 dfs 深度优先算法 深度优先搜索的步骤分为 1.递归下去 2.回溯上来。顾名思义&#xff0c;深度优先&#xff0c;则是以深度为准则&#xff0c;先一条路走到底&#xff0c;直到达到目标。这…