Shopify / liquid

Liquid markup language. Safe, customer facing template language for flexible web apps.
https://shopify.github.io/liquid/
MIT License
11.15k stars 1.4k forks source link

json_parse filter #432

Closed reggi closed 6 years ago

reggi commented 10 years ago

I'd really love a json_parse filter. The below code does not work to convert a json file to a server-side liquid object. It is however, very possible to convert a json file to a javascript variable, but it's sadly not server-side, and bad for SEO to have content rendered by the client. I was going to attempt to break down the string using liquid itself but It's pretty hard. I've created objects with liquid in the past but they're quite crude you can check that out here, the tweet, the gist example.

This is so simple for you guys to implement into production and it's a no brainer.

{% capture quotes %}
  {% include "json.quotes" %}
{% endcapture %}

{% assign quotes = quotes.quotes[0].author | json_parse %}
{{ quotes.quotes[0].author }}

<script>
var quotes = JSON.parse({{ quotes | json }});
document.write(quotes.quotes[0].author);
</script>

I just tried to use the new theming internationalization feature, I couldn't get it to work.

Ideally this type of thing could just be done with locales. But it would still be nice to have a json_parse.

fw42 commented 10 years ago

you can easily do this using a custom liquid drop that wraps json files

reggi commented 10 years ago

@fw42 Hmm, just realized this is the repo for all of liquid not shopify's implementation on http://shopify.com right? Ugh... I gotta find a better place for this request.

fw42 commented 10 years ago

so just so I understand what you want... You want to store a json file within your shopify theme editor, and then be able to use the parsed data within other Liquid templates in some easy way?

reggi commented 10 years ago

I want to create a liquid variable that is an object that has the contents of the parsed data. Yeah, I think you got it.

nickpearson commented 10 years ago

I use this in my own projects (with a custom parse_json filter and a custom json tag), and it's very useful, even when the data is inlined. For example:

{% json states %} {
  "AL": "Alabama",
  "AK": "Alaska",
  "AZ": "Arizona"
} {% endjson %}
<select>
  {% for state in states %}
    <option value="{{ state[0] }}">{{ state[1] }}</option>
  {% endfor %}
</select>

Or from some external source (json_string here):

{% assign data = json_string | parse_json %}
...

Since I control the Liquid code in the projects where I use this, I haven't considered all of the security implications, but the only one I can think of off the top of my head would be parsing a maliciously-large JSON string.

fw42 commented 10 years ago

I've never heard that idea before, so I would guess that there is not that much demand for it at Shopify, but then, the idea doesn't sound too crazy to me. @Shopify/liquid, thoughts?

fw42 commented 10 years ago

Many people actually complain that it's not possible to "create objects" in Liquid. This would at least somehow satisfy that demand in a very generic way.

fw42 commented 10 years ago

@carolineschnapp, @boourns, what do you think about this?

nickpearson commented 10 years ago

I can say that the ability to create and modify objects is huge in the way I often use Liquid. A few of my projects have filters for creating arrays and hashes and populating them (e.g., push, put, shift, etc.).

This can open up systems to modification of data structures that have so far been immutable through Liquid and thus safe to expose to Liquid code. For example, if a CMS has a pages array, and a push filter is exposed, the array could contain the wrong data when it is used later, either inside Liquid or elsewhere in the system. But, such filters could be made to operate only on objects tagged (or wrapped) with an indication that they are explicitly Liquid-mutable.

All that being said, if the security and data integrity implications are well considered, this could be a big way to make Liquid usable for a wider range of applications.

Just my two cents. :)

carolineschnapp commented 10 years ago

We can't create arrays now, unless using the split filter:

   {% assign my_array = 'Banana,Apple,Organic carrot' | split: ',' %}

Whatever we add here, we ought to be able to create arrays too.

In Shopify's Liquid, we can turn Liquid objects - some at least - to JSON, which has been extremely useful, example:

   var product = {{ product | json }};

The other way around would be nice, I guess. I have not met with anyone requesting this before.

fw42 commented 10 years ago

stealing nicks example, you could potentially do something like

{% json my_array %}
[ "banana", "apple", "organic carrot" ]
{% endjson %}
boourns commented 10 years ago

This is impossible in anything but the trivial case.

For example, in javascript I could do an AJAX GET from a server, and then try to convert it to a liquid object.

{% json my_array %}
 $.get("server.json")
{% endjson %}

Since the JSON is calculated client-time, and liquid is server-side (and cached after the fact), this won't work.

fw42 commented 10 years ago

@boourns: I think the original post was about storing fixed json things in a theme asset or stuff like that

boourns commented 10 years ago

Right, but we need to be careful to make sure that we're not creating impossible expectations.

reggi commented 10 years ago

@boourns nope, I have very possible expectations @fw42 nailed it, my idea is storing fixed json and having it accessible by the server via liquid variables. :cake: Not that hybrid ajax request thing you created @boourns

tobi commented 10 years ago

perhaps assign could simply learn how to deal with json data by detecting a { as first character?

On Wed, Aug 27, 2014 at 4:11 PM, Thomas Reggi notifications@github.com wrote:

@boourns https://github.com/boourns nope, I very possible expectations @fw42 https://github.com/fw42 nailed it, my idea is storing fixed json and having it accessible by the server via liquid variables. [image: :cake:]

— Reply to this email directly or view it on GitHub https://github.com/Shopify/liquid/issues/432#issuecomment-53633198.

jankuca commented 10 years ago

+1

Will this be possible? (I mean in the shopify.com implementation) I actually really need to parse JSON strings as I have a specially tailored blog post editor which saves JSON as blog post content and I need to render the result in the templates.

I'd be storing HTML as usual but I really want the freedom to change the rendering independently on the database.

I need something like this:

<article>
{% assign elements = article.content | parse_json %}
{% for element in elements %}
<div class="{{ element.class }}">
  {% case element.class %}
  {% when 'img' %}
    <img src="{{ element.data.url }}">
  …
  {% endcase %}
</div>
{% endfor %}
fw42 commented 10 years ago

why don't you just change the Article#content method to do the json parsing for you? why does this need to happen in Liquid-land?

jankuca commented 10 years ago

@fw42 Can I do that from a Shopify theme?

fw42 commented 10 years ago

no

fw42 commented 10 years ago

sorry I just saw your edit.. didn't get that you are talking about shopify..

jankuca commented 10 years ago

What would you recommend for my case? I am pretty much left with rendering the article content on the client via JS which kind of sucks because of search engines and similar.

Would you consider implementing this on your side in some form?

fw42 commented 10 years ago

I will bring this up for discussion internally and let you know.

reggi commented 10 years ago

@jankuca's use case case is a pretty good one too! @fw42 I'd love to hear what the internal chatter is :)

reggi commented 10 years ago

I'm totally at a loss, I started using en.defaults.json in a very unconventional way, noticed today that you're not allowing arrays, integers, and bools. It's messing up my build process. Can we please, please, please get a json_parse filter stat??

carolineschnapp commented 10 years ago

@reggi

en.default.json in the /locales folder? That's parsed by the Language page, for translation. You can't use that in an unconventional manner.

reggi commented 10 years ago

Hey @carolineschnapp!

I'm generating liquid files via ejs template with node.js all built around hacking the en.defaults.json file, so I can use it as one big data-store / CMS.

You can see in that template file that I'm pulling in data from en.defaults using that t filter. That whole template generates a liquid file that contains all the variables I need to pass it into a view component. Then I just need to call the id, controller, and view and it renders the HTML with all the variables.

Next step would be taking deprecating that en.defaults.json file out into smaller json files, and building liquid files with the data directly in it rather then pulling from en.defaults.json. Since there's kind of no reason to even use it anymore (I didn't start out generating liquid with node).

I'd still love json_parse :)

tyteen4a03 commented 9 years ago

Since settings.html does not allow Javascript, without json_parse I am required to use CSV if I want an array of values. I really prefer JSON over CSV.

Please add json_parse.

robinbortlik commented 9 years ago

I was able to achieve this functionality by creating parse_json filter and then assign this result to variable in the template. Then you can simple access data as it should be an object. Here is the commit I made to locomotive_cms liquid extension. https://github.com/robinbortlik/liquid_extensions/commit/2b7a67281b55f25045ec4c44813ddd7535b00b3d

{% for event in contents.events %}
   {% assign json = event.content | as_json %}
   {% for ticket in json.tickets %}
       {{ ticket.end_date }}
  {% endfor %}
{% endfor %}
tyteen4a03 commented 9 years ago

Great, hopefully Shopify will add it soon.

rickydazla commented 9 years ago

I would like this. For Shopify, tons of useful Variant-related yet non-price affecting data could be stored and used to manipulate the front-end, then tie in with Line Item Properties... :sweet_potato:

horsejockey commented 9 years ago

Any news on this? I would love to store json data in shopify metafields and then be able to parse it as liquid server side. I can think of lots of awesome applications.

coltonbrugger commented 8 years ago

What @horsejockey said!

I would love to store json data in [insert liquid-served tools'] metafields and then be able to parse it as liquid server side.

sandect commented 8 years ago

Very helpful, need it!

Panksy commented 8 years ago

I would also really like this. At the moment I am building and parsing a custom string array with unusual chars as delimiters.. Json would be so much nicer!

mrpunkin commented 8 years ago

Yes, please! This would be a great solution for all sorts of various problems.

ozzyogkush commented 8 years ago

The problem is that people will always use your system in unintended ways. In my case, I'm a software engineer being forced to use Jekyll - and from my perspective there are many things missing. Being able to create an array or hash in liquid directly seems like a no-brainer, as well as the ability to parse between types without requiring individual plugins. Just my 0.02.

kainjow commented 8 years ago

Two things I think could be added:

  1. json_parse as mentioned by everyone else, doesn't seem difficult based on @robinbortlik's example
  2. Creating hashes and arrays in Liquid directly, by looking for a starting { or [ character. For example:
{% assign names = ['Steve', 'Bill', 'Larry'] %}

Or:

{% assign person = {'name': 'Steve', age: 60} %}

Hash keys could be expressions that result in a string or int, and hash values and array items could be expressions resulting in any value. The Liquid strict parser could handle this pretty easily.

reggi commented 8 years ago

@kainjow ideally with json_parse the you'd get what your talking about and the following would work.

{% assign array = '["Steve", "Bill", "Larry"]' | json_parse %}
{{ array[0] }} // Steve
{% assign object = '{"name": "Steve", "age": 60}' | json_parse %}
{{ object.name }} // Steve

@fw42 Any idea if we'll ever get a json_parse filter?

The main idea is the filter json is like javascript's JSON.stringify, and json_parse would be like JSON.parse. Seems like a simple idea that would allow people to simple create arrays and objects in liquid.

kainjow commented 8 years ago

@reggi yes, json can do it, but you're limited to hard coded strings. What if you want to use a variable exposed to Liquid already? Built-in support for the syntax would be far more powerful.

reggi commented 8 years ago

@kainjow totally, It's not fun but there are ways... 😬

{% assign jsonString = "" %}
{% assign jsonString = jsonString | append: "[" %}

{% for tag in product.tags %}
        {% assign jsonString = jsonString | append: '"' %}
        {% assign jsonString = jsonString | append: tag %}
        {% assign jsonString = jsonString | append: '"' %}
    {% if forloop.last == true %}

    {% else %}
        {% assign jsonString = append: "," %}
    {% endif %}
{% endfor %}

{% assign jsonString = jsonString | append: "]" %}

{% assign yayJson = jsonString | json_parse %}

And building an object like this would be much worse.

I feel like the real reason we can't make objects / arrays is it would put too much on Shopify servers, and the rendering engine. 😢 So I doubt we'd ever get it via json_parse or a native way of doing it.

patrickleet commented 8 years ago
function liquidToJSON(liquidString) {
  return JSON.parse(liquidString.replace(/=>/g, ': ').replace(/(\s)nil(\s|,)/g, '"nil",').replace(/Liquid::ImageDrop/g, '"Liquid::ImageDrop"').replace(/\}(|\s+){/g, '},{'));
} 
function JSONtoLiquid(json) {
  return JSON.stringify(json).replace(/\"(|\s+):(|\s+)/g, '"=>').replace(/"nil",/g, 'nil').replace(/"Liquid::ImageDrop"/g, 'Liquid::ImageDrop').replace(/\},(|\s+){/g, '}{');
}
evulse commented 8 years ago

For anyone else tired of waiting for this

https://github.com/culturekings/shopify-json-parser

jnorth commented 7 years ago

I'm also coming at this from a Shopify theme developer perspective, but I think a way to build up liquid objects through something like json_parse would be great for mocking objects. For example, this could be used for mocking up Shopify objects for section on-boarding.

{% if onboarding %}
  Create a mock article object
  {% json article %}
    {
      "title": {{ 'onboarding.article-excerpt.title' | t | json }},
      "content": {{ 'onboarding.article-excerpt.content' | t | json }}
    }
  {% endjson %}
{% endif %}

<div class="article-excerpt">
  <h1 class="article-excerpt-title">{{ article.title }}</h1>
  <div class="article-excerpt-content">{{ article.content }}</div>
</div>

Here's a longer example: https://gist.github.com/jnorth/2202368d02e9425d54e9e0bb3ee66887

evulse commented 7 years ago

Solution for this in Pull Request #867 we have been using the liquid side JSON parsing but we have hit slow downs on our site which everyone knows means lost sales so we are proposing the attached to bring json parsing into the standard filter functions.

celsowhite commented 7 years ago

Would love this feature. I'm currently trying to set a metafield value as a JSON object. This would be helpful when I'm using js to create a metafield using the API then easily access key/values of that object in Liquid.

I realize I can create a bunch of different metafields with different keys and values but this would help make my requests and code more concise.

rafaelsorto commented 6 years ago

Hi guys, what an interesting read I just had. Any news on the json_parser ?

I have a customer who is importing a lot of data via Airtable API from different tables and storing the file via Asset API inside the theme, but then there is no way to translate that file into shopify variables.

I had to read the file via javascript using getJSON and then doing everything else in Javascript Land. It would be very nice to have a Liquid Option.

Regards, Rafael

stephengardner commented 6 years ago

Another half-year and no update here. I certainly need this feature. Any update?

Cellis9421 commented 6 years ago

This filter would make a world of difference to so many developers, myself included.

This issue was a short novel, but I think we can all agree there are an incredible amount of use cases relating to both development and design where the json_parse filter would be useful. JSON has become increasingly more so the 'go-to' standard since the beginning of this issue as cited in the numerous relationships between design and JSON data structures.

Regards, {{ "{ name: 'Calvin' }" | json_parse }}

marcsyp commented 6 years ago

image

:)