JSON with array and non-array data with Groovy JsonBuilder - arrays

I need to create a JSON string in my Groovy script which has some elements that are array and some which are not. For example the below..
{
"fleet": {
"admiral":"Preston",
"cruisers": [
{"shipName":"Enterprise"},
{"shipName":"Reliant"}
]
}
}
I found this post but the answers either didn't make sense or didn't apply to my example.
I tried the below in code...
def json = new groovy.json.JsonBuilder()
def fleetStr = json.fleet {
"admiral" "Preston"
cruisers {
{shipName: "[Enterprise]"}, {shipName: "[Reliant]"}
}
}
But it gives an exception...
Ambiguous expression could be either a parameterless closure expression or an isolated open code block

In Groovy, the {} syntax is used for closures. For objects in JSON, you want to use the map syntax [:], and for lists, the list syntax []:
def json = new groovy.json.JsonBuilder()
def fleetStr = json.fleet {
"admiral" "Preston"
cruisers( [
[shipName : "[Enterprise]"],
[shipName: "[Reliant]"]
])
}
assert json.toString() ==
'{"fleet":{"admiral":"Preston","cruisers":[{"shipName":"[Enterprise]"},{"shipName":"[Reliant]"}]}}'
Update: as per your follow-up, you need to use the same list syntax [] outside the "[Enterprise]" and "[Reliant]" strings:
def json = new groovy.json.JsonBuilder()
def fleetStr = json.fleet {
"admiral" "Preston"
cruisers( [
[shipName : ["Enterprise"]],
[shipName: ["Reliant"]]
])
}
assert json.toString() ==
'{"fleet":{"admiral":"Preston","cruisers":[{"shipName":["Enterprise"]},{"shipName":["Reliant"]}]}}'

Related

Circe: Json object to Custom Object

I've go this json node
...
"businessKeys": [
"product://color?product_code",
"product://?code"
]
...
after a sequence of computation I have a Json istance but I'm not able to understand how I can transform that object into a List of my object. So Json (istance) to List[MyClass].
MyClass(root: ParameterType, children: List[ParameterType] = List.empty)
ParameterType(parameter: String)
I have a method that transform a String into a MyClass istance, so I need to decode Json into List[String] and then call my function or there is a direct method?
Thanks
You need implement our own io.circe.Decoder, in your case it can be converted from decoder for string.
Please, find some example code below:
import io.circe._
import io.circe.generic.auto._
object CirceExample {
class ParameterType(parameter: String) {
override def toString: String = parameter
}
class MyClass(root: ParameterType, children: List[ParameterType] = List.empty) {
override def toString: String = s"MyClass($root, $children)"
}
object MyClass {
/**
* Parsing logic from string goes here, for sake of example, it just returns empty result
*/
def parse(product: String): Either[String, MyClass] = {
Right(new MyClass(new ParameterType("example")))
}
// This is what need - just declare own decoder, so the rest of circe infrastructure can use it to parse json to your own class.
implicit val decoder: Decoder[MyClass] = Decoder[String].emap(parse)
}
case class BusinessKeys(businessKeys: List[MyClass])
def main(args: Array[String]): Unit = {
val json = "{ \"businessKeys\": [\n \"product://color?product_code\",\n \"product://?code\"\n ] }"
println(parser.parse(json).map(_.as[BusinessKeys]))
}
}
Which in my case produced next output:
Right(Right(BusinessKeys(List(MyClass(example, List()), MyClass(example, List())))))
Hope this will help you!

Swift Deep Flatten Array Property [duplicate]

I'm starting to learn about closures and want to implement them in a project I'm working on and I'd like some help.
I have a class defined as follows:
class MyObject {
var name: String?
var type: String?
var subObjects: [MyObject]?
}
And I want to use closures or higher oder functions (something like flatMap comes to mind) to flatten an [MyObject] and joining all MyObject and subOjects into one array.
I've tried using [MyObject].flatMap() but this operation doesn't return the nested subObjects.
First, I would highly recommend making the type of subObjects be non-optional. There's rarely a reason for optional arrays. Do you really need to distinguish between "no array" and "an empty array?" This is very uncommon. If you make subObjects just be an array, you can write what you're describing as a simple recursive function:
func flattenMyObjects(myObjects: [MyObject]) -> [MyObject] {
return myObjects.flatMap { (myObject) -> [MyObject] in
var result = [myObject]
result.appendContentsOf(flattenMyObjects(myObject.subObjects))
return result
}
}
If you need it to be optional, the changes are minor (you'll need to add an if-let or something similar).
One approach to flattening a recursive class structure is with a recursive function.
Here is the class that we would like flattened:
public class Nested {
public let n : Int
public let sub : [Nested]?
public init(_ n:Int, _ sub:[Nested]?) {
self.n = n
self.sub = sub
}
}
Here is the function that demonstrates how this could be done:
func test() {
let h = [
Nested(1, [Nested(2, nil), Nested(3, nil)])
, Nested(4, nil)
, Nested(5, [Nested(6, nil), Nested(7, [Nested(8, nil), Nested(9, nil)])])
]
func recursiveFlat(next:Nested) -> [Nested] {
var res = [Nested]()
res.append(next)
if let subArray = next.sub {
res.appendContentsOf(subArray.flatMap({ (item) -> [Nested] in
recursiveFlat(item)
}))
}
return res
}
for item in h.flatMap(recursiveFlat) {
print(item.n)
}
}
The heart of this approach is recursiveFlat local function. It appends the content of the nested object to the result, and then conditionally calls itself for each element to add their contents as well.

How to validate empty array of strings with ajv?

I make json validation with ajv. I need to validate array of strings. I know which elements can be placed there so I make appropriate 'enum'. But in some case enum can be empty and array can be empty too. Here is simple test:
var schema = {
"type":"array",
"items" : {
"type" : "string",
"enum" : []
}
}
var data = [];
var Ajv = require('./ajv-4.1.1.js');
var ajv = Ajv({
allErrors : true
});
var validate = ajv.compile(schema);
var valid = validate(data);
if (!valid)
console.log(validate.errors);
As a result I get:
Error: schema is invalid:data.items.enum should NOT have less than 1 items, data.items should be array, data.items should match some schema in anyOf
I can add any fictive string to enum array but is it possible to validate this case in legal way? Adding 'minItems=0' restriction doesn't help.
Is it really json schema draft restriction that I can't use empty enum?
UPD: I expect to validate code in general case:
var array = Object.keys(someObj); // array: ["foo", "bar"]
var schema = {
"type":"array",
"items" : {
"type" : "string",
"enum" : array
}
}
var data = ["foo"]; // valid
var data = ["bar"]; // valid
var data = ["bar","foo"]; // valid
I expect to validate code in special case:
var array = Object.keys(someObj); // array: []
var schema = {
"type":"array",
"items" : {
"type" : "string",
"enum" : array
}
}
var data = []; // I expect to see it valid too but get error instead.
The enum keyword is required to have at least one value. The specification states ...
5.5.1.1. Valid values
The value of this keyword MUST be an array. This array MUST have at least one element. Elements in the array MUST be unique.
Elements in the array MAY be of any type, including null.
http://json-schema.org/latest/json-schema-validation.html#anchor76
This makes sense because an empty enum would mean nothing would ever validate. However, I do see how it could come in handy in your particular case. If you need to build the schema dynamically, you will need to check for the empty array case and use a different schema.
Here's one way to do it:
{
"type": "array",
"maxItems": 0
}
Here's another:
{
"type": "array",
"not": {}
}

Closures to flat nested objects?

I'm starting to learn about closures and want to implement them in a project I'm working on and I'd like some help.
I have a class defined as follows:
class MyObject {
var name: String?
var type: String?
var subObjects: [MyObject]?
}
And I want to use closures or higher oder functions (something like flatMap comes to mind) to flatten an [MyObject] and joining all MyObject and subOjects into one array.
I've tried using [MyObject].flatMap() but this operation doesn't return the nested subObjects.
First, I would highly recommend making the type of subObjects be non-optional. There's rarely a reason for optional arrays. Do you really need to distinguish between "no array" and "an empty array?" This is very uncommon. If you make subObjects just be an array, you can write what you're describing as a simple recursive function:
func flattenMyObjects(myObjects: [MyObject]) -> [MyObject] {
return myObjects.flatMap { (myObject) -> [MyObject] in
var result = [myObject]
result.appendContentsOf(flattenMyObjects(myObject.subObjects))
return result
}
}
If you need it to be optional, the changes are minor (you'll need to add an if-let or something similar).
One approach to flattening a recursive class structure is with a recursive function.
Here is the class that we would like flattened:
public class Nested {
public let n : Int
public let sub : [Nested]?
public init(_ n:Int, _ sub:[Nested]?) {
self.n = n
self.sub = sub
}
}
Here is the function that demonstrates how this could be done:
func test() {
let h = [
Nested(1, [Nested(2, nil), Nested(3, nil)])
, Nested(4, nil)
, Nested(5, [Nested(6, nil), Nested(7, [Nested(8, nil), Nested(9, nil)])])
]
func recursiveFlat(next:Nested) -> [Nested] {
var res = [Nested]()
res.append(next)
if let subArray = next.sub {
res.appendContentsOf(subArray.flatMap({ (item) -> [Nested] in
recursiveFlat(item)
}))
}
return res
}
for item in h.flatMap(recursiveFlat) {
print(item.n)
}
}
The heart of this approach is recursiveFlat local function. It appends the content of the nested object to the result, and then conditionally calls itself for each element to add their contents as well.

Groovy ConfigSlurper Configure Arrays

I am trying to create a config that would look something like this:
nods = [
nod {
test = 1
},
nod {
test = 2
}
]
and then use configSlurper to read it but the "node" objects appear to be null after the read.
Here is my code:
final ConfigObject data = new ConfigSlurper().parse(new File("config.dat").toURI().toURL())
println data.nods
and the output:
[null, null]
What am I doing wrong?
Thanks!
It think I resolved it this way:
config {
nods = [
['name':'nod1', 'test':true],
['name':'nod2', 'test':flase]
]
}
And then using it like:
config = new ConfigSlurper().parse(new File("config.groovy").text)
for( i in 0..config.config.nods.size()-1)
println config.config.nods[i].test
Hope this helps someone else!!
You have to be careful when using ConfigSlurper when doing this sort of thing.
For example your solution will actually produce the following output:
true
[:]
If you look carefully you will notice that there is a typo on the second array value flase instead of false
The following:
def configObj = new ConfigSlurper().parse("config { nods=[[test:true],[test:false]] }")
configObj.config.nods.each { println it.test }
should produce the correct result:
true
false
I tried to parse with ConfigSlurper something like this:
config {sha=[{from = 123;to = 234},{from = 234;to = 567}]}
The array "sha" was far from what expected.
To get "sha" as an array of ConfigObjects I used a helper:
class ClosureScript extends Script {
Closure closure
def run() {
closure.resolveStrategy = Closure.DELEGATE_FIRST
closure.delegate = this
closure.call()
}
}
def item(closure) {
def eng = new ConfigSlurper()
def script = new ClosureScript(closure: closure)
eng.parse(script)
}
this way I get an array of ConfigObjects:
void testSha() {
def config = {sha=[item {from = 123;to = 234}, item {from = 234;to = 567}]}
def xx = item(config)
assertEquals(123, xx.sha[0].from)
}

Resources