nodeca / js-yaml

JavaScript YAML parser and dumper. Very fast.
http://nodeca.github.io/js-yaml/
MIT License
6.3k stars 771 forks source link

Dump an object where all sequences are flow formatted without specifying a flow level? #416

Open mswolfe opened 6 years ago

mswolfe commented 6 years ago

I have an object that I need to dump to YAML which contains many nested objects (5+ levels deep). I do not want to specify the flowLevel because I would like everything but the YAML sequences (JS array's) to be written in block form.

I have tried setting the style option for the '!!seq' tag, but i don't see any difference in the output. For example: const yamlExportedString = yaml.dump(objectToWrite, { lineWidth: 999999, sortKeys: true, styles: { '!!seq': 'flow' }});

Am I setting this correctly? Is block versus flow formatting only based on the flowLevel option?

Thank you in advance for your help!

mswolfe commented 6 years ago

Update:

From what I can tell there are no styles available for sequences because they would be specified in the type definition here (https://github.com/nodeca/js-yaml/blob/master/lib/js-yaml/type/seq.js) similar to how the styles for the int type are specified here: https://github.com/nodeca/js-yaml/blob/master/lib/js-yaml/type/int.js#L160-L164

I have gotten it to work by adding a new state variable to force sequences into flow mode. You can see the code here: https://github.com/mswolfe/js-yaml/pull/1

Please let me know if this feature is something that belongs here or if there is a different way to achieve the results. I'm happy to work on this code and submit an official PR to get this change merged in.

Thanks!

garyo commented 5 years ago

I'd like this as well. I'm dumping 3d graphics state and having every matrix and vector come out one element per line is super verbose looking. I'd much prefer them in flow style, but they're not always at the same level so I'd need something like @mswolfe 's solution. Specifically in my case I'd like all sequences that don't contain objects (i.e. just strings & numbers) to be in flow style.

AlexHolly commented 5 years ago

@garyo

Maybe like this? https://github.com/nodeca/js-yaml/blob/3db03f295865cf58ec9f4257894b55a6ad52e9ed/lib/js-yaml/dumper.js#L739

let anyObject = false
for(let i of state.dump) {
  if(_toString.call(i) === '[object Object]') {
    anyObject = true
    break
  }
}
if (block && (state.dump.length !== 0) && anyObject) {

EDIT: This creates an issue in cases with STYLE_FOLDED example: https://codepen.io/alexholly/pen/jONLzLj?editors=1010

ratacat commented 5 years ago

I'd really appreciate this as well.

garyo commented 4 years ago

@AlexHolly yes that looks exactly like what I'd like. I don't use folded style but if there's an update to your patch that doesn't have that issue, perhaps that could be included in js-yaml?

rlidwka commented 3 years ago

I'm dumping 3d graphics state and having every matrix and vector come out one element per line is super verbose looking.

You can use this hack at your own risk:

const yaml = require('js-yaml');

function Format(data) {
  if (!(this instanceof Format)) return new Format(data);
  this.data = data;
}

let CustomFormatType = new yaml.Type('!format', {
  kind: 'scalar',
  resolve: () => false,
  instanceOf: Format,
  represent: f => f.data
});

let schema = yaml.DEFAULT_SCHEMA.extend({ implicit: [ CustomFormatType ] });

function replacer(key, value) {
  if (Array.isArray(value) && !value.filter(x => typeof x !== 'number').length) {
    return Format(yaml.dump(value, { flowLevel: 0 }).trim());
  }
  return value;
}

console.log(yaml.dump({
  matrix: [
    [ 4, -7, 5, 0 ],
    [ -2, 0, 11, 8 ],
    [ 19, 1, -3, 12 ]
  ]
}, { schema, replacer }));

/* outputs:

matrix:
  - [4, -7, 5, 0]
  - [-2, 0, 11, 8]
  - [19, 1, -3, 12]
*/
vincesp commented 2 weeks ago

Another approach could be to have something like JSON.rawJSON(), e.g., yaml.rawYAML(), which unlike JSON.rawJSON() accepts any valid YAML string.

function replacer(_, value) {
  if (Array.isArray(value) && value.every(v => typeof v === 'number')) {
    return yaml.rawYAML(yaml.dump(value, { flowLevel: 0 }).trim())
  }
  return value
}

console.log(yaml.dump({
  matrix: [
    [ 4, -7, 5, 0 ],
    [ -2, 0, 11, 8 ],
    [ 19, 1, -3, 12 ]
  ]
}, { replacer }))
vincesp commented 2 weeks ago

You can use this hack at your own risk:

const yaml = require('js-yaml');

function Format(data) {
  if (!(this instanceof Format)) return new Format(data);
  this.data = data;
}

let CustomFormatType = new yaml.Type('!format', {
  kind: 'scalar',
  resolve: () => false,
  instanceOf: Format,
  represent: f => f.data
});

let schema = yaml.DEFAULT_SCHEMA.extend({ implicit: [ CustomFormatType ] });

function replacer(key, value) {
  if (Array.isArray(value) && !value.filter(x => typeof x !== 'number').length) {
    return Format(yaml.dump(value, { flowLevel: 0 }).trim());
  }
  return value;
}

console.log(yaml.dump({
  matrix: [
    [ 4, -7, 5, 0 ],
    [ -2, 0, 11, 8 ],
    [ 19, 1, -3, 12 ]
  ]
}, { schema, replacer }));

/* outputs:

matrix:
  - [4, -7, 5, 0]
  - [-2, 0, 11, 8]
  - [19, 1, -3, 12]
*/

Alternate suggestion:

import yaml from 'js-yaml'

let NumberArrayFormatType = new yaml.Type('!numberArray', {
  kind: 'sequence',
  resolve: () => false,
  instanceOf: Array,
  predicate: (obj) => obj.every((v) => typeof v === 'number'),
  represent: (obj) => yaml.dump(obj, { flowLevel: 0 }).trim(),
})

let schema = yaml.DEFAULT_SCHEMA.extend({ implicit: [NumberArrayFormatType] })

console.log(yaml.dump({
  matrix: [
    [ 4, -7, 5, 0 ],
    [ -2, 0, 11, 8 ],
    [ 19, 1, -3, 12 ]
  ]
}, { schema }));