jsongroovycouchdbhttpbuilder

Avoid converting some values to JSON with groovy's HTTPBuilder/RESTClient


I'm trying to use groovy's RESTClient to add a design document to a CouchDB database. CouchDB requires the functions within the design document (e.g. map/reduce functions within views) to be strings rather than actual JSON Function objects. Unfortunately, HTTPBuilder automatically parses the string into a JSON Function instead of preserving it as a String. Here's a simple example:

import groovyx.net.http.ContentType
import groovyx.net.http.RESTClient

RESTClient restClient = new RESTClient('http://localhost:5984', ContentType.JSON)
def databaseName = 'sampledb'
restClient.put path: "/${databaseName}" // Create the sample DB

def document = [
  language: 'javascript',
  views: [
    sample_view: [
      map: 'function(doc) { emit(doc._id, doc); }'
    ]
  ]
]
// CouchDB returns 400 Bad Request at the following line
restClient.put path: "/${databaseName}/_design/sample_design", body: document

Here is the relevant part of the CouchDB log (notice that the map function is not quoted):

[Fri, 17 Mar 2017 16:32:49 GMT] [error] [<0.18637.0>] attempted upload of invalid JSON (set log_level to debug to log it)
[Fri, 17 Mar 2017 16:32:49 GMT] [debug] [<0.18637.0>] Invalid JSON: {{error,
                                      {56,
                                       "lexical error: invalid string in json text.\n"}},
                                     <<"{\"language\":\"javascript\",\"views\":{\"sample_view\":{\"map\":function(doc) { emit(doc._id, doc); }}}}">>}
[Fri, 17 Mar 2017 16:32:49 GMT] [info] [<0.18637.0>] 127.0.0.1 - - PUT /sampledb/_design/sample_design 400
[Fri, 17 Mar 2017 16:32:49 GMT] [debug] [<0.18637.0>] httpd 400 error response:
 {"error":"bad_request","reason":"invalid_json"}

I've tried embedding quotes within the string (i.e. map: '"function(doc) { emit(doc._id, doc); }"'; this preserves the value as a string, but it also preserves the embedded quotes, which prevents CouchDB from executing the function. Does anyone know how I can preserve specific values as plain strings during the conversion to JSON?


Solution

  • I eventually found an answer to my own question. The simple solution that eluded me for about a year is to define the design document as a String instead of as a Map. The following slight modification of the example script in the question works as intended:

    import groovyx.net.http.ContentType
    import groovyx.net.http.RESTClient
    
    RESTClient restClient = new RESTClient('http://localhost:5984', ContentType.JSON)
    def databaseName = 'sampledb'
    restClient.put path: "/${databaseName}" // Create the sample DB
    
    def document = /
    {
      "language": "javascript",
      "views": {
        "sample_view": {
          "map": "function(doc) { emit(doc._id, doc); }"
        }
      }
    }
    /
    // CouchDB no longer returns 400 Bad Request at the following line and now correctly parses the
    // design document!
    restClient.put path: "/${databaseName}/_design/sample_design", body: document