简体   繁体   中英

Ruby to_yaml stringifies my json

I am trying to convert a ruby hash to yaml. I'd like part of the hash be valid json; however, when I try to serialize the json string, it is converted to yaml in quotes.

For example, when I just have a simple string, the ouput is as follows (note foo is not in quotations):

request = {}
request['body'] = 'foo'
request.to_yaml # outputs: body: foo

However, when I add something to the beginning of the string, like { foo the output for body gets quoted:

request['body'] = '{ foo'
request.to_yaml # outputs: body: '{ foo'

How can I get around this? I've tried JSON.parse and, though that make work, I can't be guaranteed that this input will actually be json (could be xml, etc...) -- I just want to give back whatever was given to me but not "stringified".

Basically, I want to give an object that looks like:

{ 'request' => {
    'url' => '/posts',
    'method' => 'GET',
    'headers' => [
      'Content-Type' => 'application/json'
    ]
  },
  'response' => {
    'code' => 200,
    'body' => '[{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]'
  }
}

Which returns:

request:
    url: /posts
    method: GET
    headers:
        - Content-Type: application/json

response:
    code: 200
    body:
        [{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]

The reason being: right now, I can go from yaml to the correct ruby hash but I can't go the other way.

The method my_hash.to_yaml() just takes a hash and converts it to YAML without doing anything special to the values. The method does not care whether your string is JSON or XML, it just treats it as a string.

So why is my JSON being put into quotes when other strings aren't?

Good question! The reason is simple: curly braces are a valid part of YAML syntax.

This:

my_key: { sub: 1, keys: 2}

Is called flow mapping syntax in YAML, and it allows you make nested mappings in one line. To escape strings which have curly braces in them, YAML uses quotes:

my_key: "{ sub: 1, keys: 2}" # this is just a string

Of course, the quotes are optional for all strings:

my_key: "foo" #same as my_key: foo

Okay, but I want to_yaml() to find my JSON string and convert it to YAML mappings like the rest of the hash.

Well then, you need to convert your JSON string to a hash like the rest of your hash. to_yaml() converts a hash to YAML. It doesn't convert strings to YAML. The proper method for doing this is to use JSON.parse, as you mentioned:

request['body'] = JSON.parse( '{"id":"ef4b3a"}' )

But the string might not be JSON! It might be XML or some other smelly string.

This is exactly why to_yaml() doesn't convert strings. A wise programmer once told me: "Strings are strings. Strings are not data structures. Strings are strings."

If you want to convert a string into a data structure, you need to validate it and parse it. Because there's no guarantee that a string will be valid, it's your responsibility as a programmer to determine whether your data is JSON or XML or just bad, and to decide how you want to respond to each bit of data.

Since it looks like you're parsing web pages, you might want to consider using the same bit of data other web clients use to parse these things:

{ 'request' => {
    'url' => '/posts',
    'method' => 'GET',
    'headers' => [
      'Content-Type' => 'application/json' #<== this guy, right here!
    ]
  },
  'response' => {
    'code' => 200,
    'body' => '[{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]'
  }
}

If the content-type doesn't agree with the body then you should throw an error because your input data is bad.

The reason '{ foo' requires quote is because this is part of the YAML specification 7.3.3 Plain Style.

Excerpt

Plain scalars must never contain the “: ” and “#” character combinations. Such combinations would cause ambiguity with mapping key: value pairs and comments. In addition, inside flow collections, or when used as implicit keys, plain scalars must not contain the “[”, “]”, “{”, “}” and “,” characters. These characters would cause ambiguity with flow collection structures.

Based on the above even your stated "return" value is incorrect and the body is probably enclosed in single quotes eg

response:
  code: 200
  body: '[{"id":"ef4b3a","title":"this is the title"},{"id":"a98c4f","title":"title of the second post"}]'

Otherwise it would create ambiguity with "Flow Sequences" ( [ , ] ) and "Flow Mappings" ( { , } ).

If you would like result of the JSON , XML or other notation language to be represented appropriately (read objectively) then you will need to determine the correct parser (may be from the "Content-Type") and parse it before converting it YAML

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM