I'm trying to create a crude json outputter that takes in a list of key-value pairs and results in the appropriate json. Here's what I've got:
class keyValuePairs {
private String[][] contents
keyValuePairs(String[][] inputList) {
contents = inputList
}
public String json() {
String jsonString = '{\n'
String[] keyVals = contents collect {jsonPair it}
jsonString += keyVals.join(',\n') + '\n'
jsonString += '}'
return jsonString
}
String jsonPair(String[] keyVal) {
return ' "' + keyVal[0] + '": "' + keyVal[1] + '"'
}
}
My keyValuesPairs takes in a list of lists of Strings and this compiles fine, but my contents collect {jsonPair it} line fails with:
Caught: groovy.lang.MissingMethodException: No signature of method: auditconfigprototype.keyValuePairs.jsonPair() is applicable for argument types: (auditconfig
prototype.keyValuePairs) values: [auditconfigprototype.keyValuePairs@7fa98a66]
Possible solutions: jsonPair([Ljava.lang.String;), json()
I could fall back on a loop over contents here, but I want to take the functional approach of using collect . However, it seems every attempt (even trivial ones!) to do a collect on my contents list fails. Have I defined the list wrong? How should I use collect over a list of lists of strings?
The exception you're getting is due to this line:
String[] keyVals = contents collect {jsonPair it}
At first glance it may appear to be the equivalent of:
String[] keyVals = contents.collect {jsonPair it}
But in fact, you're getting this:
String[] keyVals = contents(collect() {jsonPair it})
When assigning the keyVals variable, the collect() method is being called on the keyValuePairs instance; on this .
collect() then iterates through a List containing a single item, the instance of keyValuePairs , and calls the closure with the keyValuePairs instance. The jsonPair() method is expecting a String array, but receives a keyValuePairs instead. That's what is throwing the exception:
groovy.lang.MissingMethodException: No signature of method: keyValuePairs.jsonPair() is applicable for argument types: (keyValuePairs) values: [keyValuePairs@63169fe0]
The solution is to simply add the missing period to make the code as follows:
String[] keyVals = contents.collect {jsonPair it}
As an exercise, here's another way to implement your parser.
def a = [
['firstname', 'John'],
['lastname', 'Doe'],
['language', 'Groovy']
]
class KeyValuePairs {
def pairs
String json() {
new StringBuilder('{\n').with {
append(
pairs
.collect { String.format('"%s":"%s"', it[0], it[1]) }
.collect { "\t$it" }.join(',\n'))
append '\n}\n'
}.toString()
}
}
def parser = new KeyValuePairs(pairs: a)
parser.json()
This implementation uses a List < List > instead of an array because Groovy has great built-in support for them.
By using Groovy Beans , the KeyValuePairs instance is created without explicitly creating a boiler-plate constructor.
A StringBuilder is used instead of String concatenation. Mainly because I think it makes the code look nicer.
Since the formatting requires double-quotes, I used String.format() . That keeps the formatting easy to read. Note: normally I'd use a GString .
Because the formatting is applied so concisely, I did away with the jsonPair() method.
The first collect() produces an output like this:
[
'"firstname":"John"',
'"lastname":"Doe"',
'"language":"Groovy"'
]
And the second collect() adds a TAB in front of the double-quotes of every pair. Finally, the join() builds the single String of pairs.
Another way to improve the code some more is to replace
it[0], it[1]
with
it.key, it.value
That can be done by replacing the first collect() with these two:
.collect { [['key', 'value'], it].transpose().collectEntries() }
.collect { String.format('"%s":"%s"', it.key, it.value) }
I hope this helps :)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.