Nunjucks: Printing an object

Created on 13 Jun 2013  路  22Comments  路  Source: mozilla/nunjucks

Currently, if I output an object using nunjucks, I get "[object Object]":

Nunjucks:
context: {myObj: {}}
template: {{ myObj }}
output: "[object Object]"

Jinja:
context: {myObj: {}}
template: {{ myObj }}
output: "{}"

Is this desired behaviour for people? If people are interested, I can look into creating a patch to better mimic the Jinja functionality. (Possibly just dump the output from JSON.stringify)

More than anything else, this could be useful for debugging.

enhancement

Most helpful comment

Is very simple:

{{ data.products | dump | safe }}

All 22 comments

If the object has a toString method that would be used instead. The behavior you're seeing is caused by both libraries using the platform's "native" typecasting mechanism.

The other reason is that Python has a dedicated dict type, while dicts in JS are objects. If you tried to output a plain old Python class instance, you'd get the value of __unicode__(), which would probably be __repr__() if it hasn't been overridden. That would yield some equally ugly results and leads to another rabbit hole.

What exactly is your use case, aside from debugging?

Mainly debugging - the intention is to allow end-users the ability to create templates. The ability to see what's available on an object by going {{ myObj }} is pretty useful.

IMO writing your own debug filter is a better approach than expecting nunjucks to do this out of the box.

:+1: I think this is a reasonable feature considering the other option doesn't give the end user anything but "Hey you gave me an object"

Unfortunately though implementing this might conflict with the custom toString of that object (not sure if it even has the toString feature)

If it'd be possible to only call JSON.stringify() when it's a plain object, and not override any custom toString() then that'd be pretty great

You could probably check what toString() returned, and if it was [object Object] then stringify it

@dhendo That's a great idea didn't even think of that. Solves my only concern.

I'm not convinced of the usefulness of this feature.

  1. If an object is mistakenly used in lieu of a string, instead of something like [object Object] being printed, a potentially enormous blob of text gets output in production. While still not what the developer probably wanted, it's more likely to cause layout issues.
  2. A JSON-encoded version of an object is not necessarily useful if the object only has members on its prototype. In that case, you'd only see {}. A custom implementation of JSON.stringify could be written, but that's a waste of bytes for the 99% of developers that will never use this feature.
  3. Detecting objects breaks the use case for lazily-evaluated strings. If an object is detected instead of a string and the toString method is not used, there's no way to generate a string representation of an object on the fly. As @Nate-Wilkins mentioned, this could be a real hassle.
  4. Calling toString might have side effects (or a serious performance penalty), so sniffing it is not desirable. Performing this check for every object seems like a bad idea.
  5. At best, this is a debugging tool. Passing an object to JSON.stringify or writing a custom filter as it's needed is simple enough. Having a blob of JSON ooze out of a rendered template by default is the wrong way to approach this problem.
  6. Passing a string to a template prints the string, not the JSON equivalent. Passing an object would print the JSON encoded version of the object, not the object itself. This behavior is simply inconsistent.
  7. This could open the doors to potential XSS issues.

It almost seems like a more appropriate feature would be to implement source maps to allow debugging.

@mattbasta

  1. I see the concern but my personal preference would have useful data show up then no data at all
  2. Good point indeed
  3. ^
  4. Agreed I think this might be a selling point for me considering this is a template engine and most of the time we're pulling in quite a few objects

Is it possible for the user to override the toString method with JSON.stringify to return itself?

Edit:
Yup. Just tried it out works fine.

var jsonToString = function () {
    return JSON.stringify(this, null, "\t");
};

I'll be happy if nunjucks provide some ability to act as Jinja (as @dhendo mentioned above). Maybe have a special tag, just idea

context: {myObj: {}}
template: {{% myObj %}}
output: "{}"

I've always just added a global dump() function to nunjucks which does console.log -- use in the views like {{ dump(somevar) }} which outputs to the console whatever that var is. Alternative is to just actually check what your outputting in your controller. Benefit is that I then I have the all the chrome tools at my disposable.

I'm also not convinced nunjucks needs to do anything more with this like jsonifying, that seems to be going too far. It's more of a development issue imo, which is easily solved by a custom dump or similar method.

Which version it's added to? I'm using "nunjucks": "^1.2.0" seems not to be there.

@alexbeletsky dump was a custom function he added. I just added at as a builtin filter, so you can do {{ foo | dump }} now and it just calls JSON.stringify

For anyone else stumbling upon this issue, chances are youre trying to send JSON out to the view so you can use it in client side Javascript.

Router

return res.render('login', {
    title: 'Login',
    validationErrors: JSON.stringify(err.validationErrors)
});

View

<script type="text/javascript">
  console.log({{ validationErrors | safe  }});
</script>

You should avoid marking JSON output as |safe, since it can still contain a string with a closing script tag.

What's the alternatives? Everything else escapes and gives me &quot; also my JSON is not user created so it will never have malicious data in it.

here is the filter I use to escape double quotes in JSON (dependency on lodash):

nunjucksEnv.addFilter('json', function JSONstringify(obj) {
  function objectToSafeStrings(toStringify) {
    if(Array.isArray(toStringify)) {
      return toStringify.map(function(o) {
        return objectToSafeStrings(o);
      });
    } else if (_.isObject(toStringify)) {
      return _.mapValues(toStringify, value => {
        if (_.isObject(value)) {
          return objectToSafeStrings(value);
        } else if (_.isString(value)) {
          return value.replace(/"/g, '\\"');
        }
        return value;
      });
    }
    return toStringify;
  }
  return JSON.stringify(objectToSafeStrings(obj));
});

It appears this issue has been resolved in nunjucks v3.0.0-dev2 when using the built-in filter dump | safe

I prefer this, which is tabified:

// json formatting
nunjucksEnv.addFilter( 'json', function ( obj ) {
    var json = JSON.stringify( obj, null, '\t' );

    return new Nunjucks.runtime.SafeString( json );
} );

Is very simple:

{{ data.products | dump | safe }}

I hate to drag up an old issue, but I ran into trouble with dump when trying to dump all the data in a Post object because a handful of properties contain circular references. Would you be open to a PR that uses util.inspect instead? I'm not sure about the best way to handle the spaces argument in a non-breaking way, but one option could be to fall back to util.inspect if JSON.stringify() throws an exception.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

kud picture kud  路  3Comments

popomore picture popomore  路  6Comments

ricordisamoa picture ricordisamoa  路  5Comments

boutell picture boutell  路  6Comments

thisKai picture thisKai  路  3Comments