I'm trying to parse nested JSON object with Circe library. I would like to map it to flat case class ignoring some of the fields.
import io.circe.generic.auto._
import io.circe.{Decoder, Encoder, HCursor, Json}
val jsonString = """{
"parent" : {
"name" : "title",
"items" : [
{
"foo" : "",
"attrs" : {
"attrA" : "",
"attrB" : ""
}
},
{
"foo" : "",
"attrs" : {
"attrA" : "",
"attrB" : "",
"attrC" : ""
}
}]
}
}"""
// Notice I don't care about "attrC"
case class Item(foo: String, attrA: String, attrB: String)
case class Parent(name: String, items: List[Item])
implicit val testDecoder: Decoder[Item] = Decoder.instance { c =>
val itemsC = c.downField("parent").downField("items")
for {
foo <- itemsC.get[String]("foo")
a <- itemsC.downField("attrs").get[String]("attrA")
b <- itemsC.downField("attrs").get[String]("attrB")
} yield Item(foo, a, b)
}
val decodingResult = parser.decode[Parent](jsonString)
result:
Either[io.circe.Error,Parent] = Left(DecodingFailure(Attempt to decode value on failed cursor, List(DownField(name))))
I find it easier to use the auto-parser, get the data to Scala, and continue from there
import io.circe.generic.auto._
import io.circe.parser._
val sample="""{
"parent" : {
"name" : "title",
"items" : [
{
"foo" : "",
"attrs" : {
"attrA" : "",
"attrB" : ""
}
},
{
"foo" : "",
"attrs" : {
"attrA" : "",
"attrB" : "",
"attrC" : ""
}
}
]
}
}"""
case class Data(parent : Parent)
case class Parent(name: String, items: List[Item])
case class Item(foo: String, attrs : Attrs)
case class Attrs(attrA: String, attrB: String) // you don't need attributes you don't use
val data=decode[Data](sample)
Related
I have the JSon response as given below.
If metadata's Organic=true then label='true-Organic', else label='non-Organic'
in the end => return List or Map[modelId,label]
import net.liftweb.json.{DefaultFormats, _}
object test1 extends App {
val json_response =
"""{
"requestId": "91ee60d5f1b45e#316",
"error": null,
"errorMessages": [
],
"entries": [
{
"modelId":"RT001",
"sku": "SKU-ASC001",
"store": "New Jersey",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-01-22T01:25Z",
"organic": "true"
}
},
{
"modelId":"RT002",
"sku": "SKU-ASC002",
"store": "livingstone",
"ttlInSeconds": 8000,
"metadata": {
"manufactured_date": "2019-10-03T01:25Z",
"organic": "false"
}
}
] }"""
tried like this :
val json = parse(json_response)
implicit val formats = DefaultFormats
var map = Map[String, String]()
case class Sales(modelId: String, sku: String, store: String, ttlInSeconds: Int, metadata:
Map[String, String])
case class Response(entries: List[Sales])
val response = json.extract[Response]
After this, not sure how to proceed.
This is a straightforward map operation on the entries field:
response.entries.map{ e =>
e.modelId ->
if (e.metadata.get("organic").contains("true")) {
"true-Organic"
} else {
"non-Organic"
}
}
This will return List[(String, String)], but you can call toMap to turn this into a Map if required.
I'm new to Scala and I'm learning Scala and Play Framework:
I'm trying to dynamically create a Json with play/scala starting from a sequence of data named "tables" by using Map(...), List(...) and Json.toJson(...).
My result should be like the code resultCustomJsonData shown below
var resultCustomJsonData = [
{
text: "Parent 1",
nodes: [
{
text: "Child 1",
nodes: [
{
text: "Grandchild 1"
},
{
text: "Grandchild 2"
}
]
},
{
text: "Child 2"
}
]
},
{
text: "Parent 2"
},
{
text: "Parent 3"
},
{
text: "Parent 4"
},
{
text: "Parent 5"
}
];
my scala code is this below:
val customJsonData = Json.toJson(
tables.map { t => {
Map(
"text" -> t.table.name.name, "icon" -> "fa fa-cube", "nodes" -> List (
Map( "text" -> "properties" )
)
)
}}
)
but i'm getting this error:
No Json serializer found for type Seq[scala.collection.immutable.Map[String,java.io.Serializable]]. Try to implement an implicit Writes or Format for this type.
Here is a way to do it without using temporary Map:
import play.api.libs.json.Json
val customJsonData = Json.toJson(
tables.map { t =>
Json.obj(
"text" -> t.table.name.name,
"icon" -> "fa fa-cube",
"nodes" -> Json.arr(Json.obj("text" -> "properties"))
)
}
)
I think you should try implement custom serializer/writer. Check here.
For example:
implicit val userWrites = new Writes[User] {
def writes(user: User) = Json.obj(
"id" -> user.id,
"email" -> user.email,
"firstName" -> user.firstName,
"lastName" -> user.lastName
)
}
#ApiResponses(Array(
new ApiResponse(code = 200, message = "OK", response = classOf[ResultBase]),
new ApiResponse(code = 500, message = "Internal server error"),
))
and this is my trait:
#ApiModel(value="Event", description="Base class for events")
sealed trait ResultBase {
#(ApiModelProperty #field)(value = "Id")
val id: String
}
This is what I get in swagger.json
"responses" : {
"200" : {
"description" : "OK",
"schema" : {
"$ref" : "#/definitions/ResultBase"
}
},
"500" : {
"description" : "Internal server error"
}
}
However, in Swagger-ui both Schema and Schema Model are empty.
Any way around this?
How do you create a jackson custom serializer and use it in your program? The serializer is used to serialize data from a kafka stream, because my job fails if it encounters a null.
I tried the following to create a serializer.
import org.json4s._
import org.json4s.jackson.JsonMethods._
case class Person(
val user: Option[String]
)
object PersonSerializer extends CustomSerializer[Person](formats => ( {
case JObject(JField("user", JString(user)) :: Nil) => Person(Some(user))
case JObject(JField("user", null) :: Nil) => Person(None)
},
{
case Person(Some(user)) => JObject(JField("user", JString(user)) :: Nil)
case Person(None) => JObject(JField("user", JString(null)) :: Nil)
}))
I am trying to use it this way.
object ConvertJsonTOASTDeSerializer extends App
{
case class Address(street : String, city : String)
case class PersonAddress(name : String, address : Address)
val testJson1 =
"""
{ "user": null,
"address": {
"street": "Bulevard",
"city": "Helsinki",
"country": {
"code": "CD" }
},
"children": [
{
"name": "Mary",
"age": 5,
"birthdate": "2004-09-04T18:06:22Z"
},
{
"name": "Mazy",
"age": 3
}
]
}
"""
implicit var formats : Formats = DefaultFormats + PersonSerializer
val output = parse(testJson1).as[Person]
println(output.user)
}
I am getting an error saying that
Error:(50, 35) No JSON deserializer found for type com.examples.json4s.Person. Try to implement an implicit Reader or JsonFormat for this type.
val output = parse(testJson1).as[Person]
Not sure if I answer your question. I provide the runnable code:
import org.json4s._
import org.json4s.jackson.JsonMethods._
case class Person(
user: Option[String],
address: Address,
children: List[Child]
)
case class Address(
street: String,
city: String,
country: Country
)
case class Country(
code: String
)
case class Child(
name: String,
age: Int
)
val s =
"""
{ "user": null,
"address": {
"street": "Bulevard",
"city": "Helsinki",
"country": {
"code": "CD" }
},
"children": [
{
"name": "Mary",
"age": 5,
"birthdate": "2004-09-04T18:06:22Z"
},
{
"name": "Mazy",
"age": 3
}
]
}
"""
implicit val formats : Formats = DefaultFormats
parse(s).extract[Person] // Person(None,Address(Bulevard,Helsinki,Country(CD)),List(Child(Mary,5), Child(Mazy,3)))
I am trying to do some complex filtering in my app and I am to a point where I don't know what to do next. My data consistes of an array of dictionaries where the values in each of the dictionaries can be String, Int or [String].
let person1: [String : Any] = ["first_name" : "John",
"last_name" : "Smith",
"age" : 21,
"skills" : ["C#", "Java", "Swift"]]
let person2: [String : Any] = ["first_name" : "Kim",
"last_name" : "Smith",
"age" : 28,
"skills" : ["Java", "Swift"]]
let person3: [String : Any] = ["first_name" : "Kate",
"last_name" : "Bell",
"age" : 24,
"skills" : ["C#"]]
var people = [person1, person2, person3]
I let the user choose how to filter this data and create a dictionary of filter criteria. This dictionary can have any number of keys and values.
let filters: [String : [Any]] = ["age" : [28, 24],
"skills" : ["Java", "Swift"]]
In this example I want to show persons who are age 28 or 24 and have a skills of Java or Swift, which would be person2
Here is what I have so far but it only works with Int values:
for (key, values) in filters {
var filteredItems = people.filter {
var match = false
for filterValue in values {
if $0[key] as! Int == filterValue as! Int {
match = true
break
}
else {
match = false
}
}
return match
}
people = filteredItems
}
Here's how I would do this:
struct Person {
let firstName: String
let lastName: String
let age: Int
let skills: [String]
enum Filter {
enum FilterType<T: Hashable> {
case one(of: [T])
case all(of: [T])
// Match against a property that's a single value
func matches(_ value: T) -> Bool {
switch self {
case .one(let filterValues): return filterValues.contains(value)
case .all(let filterValues): return filterValues.count == 1 && filterValues[0] == value
}
}
// Match against a property that's a list of values
func matches(_ values: [T]) -> Bool {
switch self {
case .one(let filterValues): return !Set(filterValues).intersection(values).isEmpty
case .all(let filterValues): return Set(filterValues).isSuperset(of: values)
}
}
}
case age(is: FilterType<Int>)
case skills(is: FilterType<String>)
func matches(_ p: Person) -> Bool {
switch self {
case .age(let filterValues): return filterValues.matches(p.age)
case .skills(let filterValues): return filterValues.matches(p.skills)
}
}
}
}
extension Array where Element == Person.Filter {
func atLeastOneMatch(_ p: Person) -> Bool {
self.contains(where: { $0.matches(p) })
}
func matchesAll(_ p: Person) -> Bool {
self.allSatisfy { $0.matches(p) }
}
}
let people = [
Person(
firstName: "John",
lastName : "Smith",
age: 21,
skills: ["C#", "Java", "Swift"]
),
Person(
firstName: "Kim",
lastName : "Smith",
age: 28,
skills: ["Java", "Swift"]
),
Person(
firstName: "Kate",
lastName: "Bell",
age: 24,
skills: ["C#"]
),
]
let filters: [Person.Filter] = [
.age(is: .one(of: [28, 24])),
.skills(is: .one(of: ["Java", "Swift"])),
]
let peopleWhoMatchAllFilters = people.filter(filters.matchesAll)
print(peopleWhoMatchAllFilters)
let peopleWhoMatchAtLeastOneFilter = people.filter(filters.atLeastOneMatch)
print(peopleWhoMatchAtLeastOneFilter)
I've extended the filtering capability to be able to specify wether all values of a filter should be matched (e.g. a person must know Java AND Swift AND C#) or at least one (e.g. a person must know AT LEAST Java OR Swift OR C#)