<p>After some local experimentation I managed to reproduce and (hopefully) resolve.</p>
<p>Like Brad said the macro returns a Markup class that is not json decodable.
So first step is to use the <code>raw</code> filter to get the raw string (alternatively, you can call the <code>jsonSerialize</code> method on the Markup class).</p>
<p>But then I ran into another issue, where double quotes were replaced with <code>&amp;quot;</code>. This caused a syntax error when trying to decode. A <code>replace</code> filter was the only way I could get rid of those.</p>
<p>Another apparent issue is that you are trying to echo an array, which will throw an Array to string conversion exception, even when JSON decode is successful.</p>
<p>So I'd replace:</p>
<pre><code>{% set futureEvents %}
    {{ eventsMacro.futureEvents(events)|json_decode }}
{% endset %}
</code></pre>
<p>With:</p>
<pre><code>{% set futureEvents = eventsMacro.futureEvents(events) | raw | replace('&amp;quot;','&quot;') | json_decode %}
</code></pre>

CC BY-SA 4.0
2021-11-30T12:33:25.757Z
2021-11-30T13:04:30.447Z
2021-11-30T13:04:30.447Z
Matanya
<p>Guessing if you change your macro to this, it'll work:</p>
<pre><code>{{ orderedFutureEvents|json_encode|raw }}
</code></pre>
<p>The reason being that Twig will return a <a href="https://github.com/twigphp/Twig/blob/3.x/src/Markup.php" rel="nofollow noreferrer">Markup</a> class that wraps the JSON content when returning from the macro that <code>json_encode</code> doesn't know what to do with.</p>
<p>Passing the contents through <code>|raw</code> returns the original JSON.</p>

CC BY-SA 4.0
2020-10-26T21:03:06.353Z
Unfortunately, adding the raw tag to the macro variable didn't solve the issue.
When I dump what the macro returned, it's still a Markup object.
`object(Twig\Markup)#8733 (2) { ["content":"Twig\Markup":private]=> string(22297) " [{"sectionId":"5","typeId":"5", ... `
Joorren
CC BY-SA 4.0
2020-10-27T02:16:12.867Z
Can you share the full macro?
Brad Bell
CC BY-SA 4.0
2020-10-28T10:10:21.887Z
I've added the full macro to the question. (I know it's a really bad way to handle this kind of stuff)
Joorren
CC BY-SA 4.0
2020-10-28T20:14:36.730Z
Hrm... I'm out of ideas. Are you comfortable with xDebug and PHPStorm? Could debug the compiled templates to get some additional insights.
Brad Bell
CC BY-SA 4.0
2020-10-26T16:51:02.723Z
2020-10-26T16:51:02.723Z
Brad Bell
<p>I'm trying to use the <a href="https://craftcms.com/docs/3.x/dev/filters.html#json-decode" rel="nofollow noreferrer">json_decode</a> twig filter from Craft to decode an array of entry objects that got encoded in a macro.</p>
<p>In the macro, the array of entry objects gets encoded by json_encode and echoed:</p>
<pre><code>{{ orderedFutureEvents|json_encode }}
</code></pre>
<p>This is how the macro value gets set in the template:</p>
<pre class="lang-php prettyprint-override"><code>{% set events = craft.entries()
        .section('eventPost')
        .all() %}
{% import &quot;_macros/events.html&quot; as eventsMacro %}
{% set futureEvents %}
    {{ eventsMacro.futureEvents(events)|json_decode }}
{% endset %}
</code></pre>
<p>When loading the page it's used on, I get a syntax error:</p>
<blockquote>
<p>Invalid Argument – yii\base\InvalidArgumentException</p>
<p>Syntax error.</p>
</blockquote>
<p>The specific lines that trows the error:</p>
<blockquote>
<p>yiisoft/yii2/helpers/BaseJson.php at line 133</p>
<p>yiisoft/yii2/helpers/BaseJson.php at line 107</p>
</blockquote>
<p>Compiled template line:</p>
<pre class="lang-php prettyprint-override"><code>$context[&quot;test&quot;] = craft\helpers\Json::decode((isset($context[&quot;futureEvents&quot;]) || array_key_exists(&quot;futureEvents&quot;, $context) ? $context[&quot;futureEvents&quot;] : (function () { throw new RuntimeError('Variable &quot;futureEvents&quot; does not exist.', 19, $this-&gt;source); })()));
</code></pre>
<p>What's causing this error?</p>
<p>Full macro:</p>
<pre class="lang-php prettyprint-override"><code>{% macro futureEvents(events) %}
    {% set today = &quot;now&quot;|date('d/m/Y') %}
    {% set now = &quot;now&quot;|date('U') %}
    {% set futureEvents = [] %}
    {% for event in events %}
        {% for block in event.calendar.level(1).all() %}
            {% switch block.type.handle %}
                {% case 'periodOfDays' %}
                    {% if block.endDate|date('d/m/Y') &gt; today  %}
                        {% if block.startDate|date('d/m/Y') &lt; today %}
                            {# Ongoing event #}
                            {% set futureEvents = futureEvents|merge([{'date': today, 'event': event}]) %}
                        {% else %}
                            {# Future event #}
                            {% set futureEvents = futureEvents|merge([{'date': block.startDate|date('d/m/Y'), 'event': event}]) %}
                        {% endif %}
                    {% endif %}
                {% case 'specificDates' %}
                    {% set tempEvent = [] %}
                    {% set break = false %}
                    {% set empty = false %}
                    {% for child in block.children.all() if not break %}
                        {% if child.dateAndTime|date('U') &gt; now %}
                            {# Future event #}
                            {% set tempEvent = [{'date': child.dateAndTime|date('d/m/Y'), 'event': event}] %}
                            {% set break = true %}
                            {# Break because event is found #}
                        {% elseif (child.dateAndTime|date('U') + child.duration*60) &gt; now %}
                            {# Ongoing event #}
                            {% set tempEvent = [{'date': today, 'event': event}] %}
                            {% set break = true %}
                            {# Break because event is found #}
                        {% endif %}
                        {% else %}
                            {% set empty = true %}
                    {% endfor %}
                    {% if tempEvent is same as([]) and empty %}
                        {# Fallback on default #}
                    {% elseif tempEvent is not same as([]) %}
                        {% set futureEvents = futureEvents|merge(tempEvent) %}
                    {% endif %}
            {% endswitch %}
        {% else %}
            {% if event.dateAndTime|date('U') &gt; now %}
                {# Future event #}
                {% set futureEvents = futureEvents|merge([{'date': event.dateAndTime|date('d/m/Y'), 'event': event}]) %}
            {% elseif (event.dateAndTime|date('U') + event.duration*60) &gt; now %}
                {# Ongoing event #}
                {% set futureEvents = futureEvents|merge([{'date': today, 'event': event}]) %}
            {% endif %}
        {% endfor %}
    {% endfor %}
    {% set orderedFutureEvents = [] %}
    {% for key in futureEvents|sort((a, b) =&gt; a &lt;=&gt; b)|column('date', 'event')|keys %}
        {% set orderedFutureEvents = orderedFutureEvents|merge([futureEvents[key].event]) %}
    {% endfor %}
    {{ orderedFutureEvents|json_encode|raw }}
{% endmacro %}
</code></pre>

CC BY-SA 4.0
2020-10-23T20:48:40.353Z
That last bit looks wrong: })())); I think you need to fix the paranthesis.
CraftN00b
CC BY-SA 4.0
2020-10-26T03:13:46.640Z
Just trying to follow along... `orderedFutureEvents` is an array of entries that you JSON encode into a string.  Is `events` in your macro the same JSON encoded `orderedFutureEvents` that gets passed into the macro?
Brad Bell
CC BY-SA 4.0
2020-10-26T14:36:05.527Z
@BradBell the `events` parameter is an array of entry objects. The macro filters out specific entries to return a new array of entry objects. I'll update my post to make it a bit more clear.
Joorren
<json><yii><macro><twigextension>
Twig json_decode gives Syntax Error
# Twig json_decode gives Syntax Error

(Asked by: username_0 on 2020-10-23)

I'm trying to use the json_decode twig filter from Craft to decode an array of entry objects that got encoded in a macro.
In the macro, the array of entry objects gets encoded by json_encode and echoed:
{{ orderedFutureEvents|json_encode }}

This is how the macro value gets set in the template:
{% set events = craft.entries()
        .section('eventPost')
        .all() %}
{% import "_macros/events.html" as eventsMacro %}
{% set futureEvents %}
    {{ eventsMacro.futureEvents(events)|json_decode }}
{% endset %}

When loading the page it's used on, I get a syntax error:

Invalid Argument – yii\base\InvalidArgumentException
Syntax error.

The specific lines that trows the error:

yiisoft/yii2/helpers/BaseJson.php at line 133
yiisoft/yii2/helpers/BaseJson.php at line 107

Compiled template line:
$context["test"] = craft\helpers\Json::decode((isset($context["futureEvents"]) || array_key_exists("futureEvents", $context) ? $context["futureEvents"] : (function () { throw new RuntimeError('Variable "futureEvents" does not exist.', 19, $this->source); })()));

What's causing this error?
Full macro:
{% macro futureEvents(events) %}
    {% set today = "now"|date('d/m/Y') %}
    {% set now = "now"|date('U') %}
    {% set futureEvents = [] %}
    {% for event in events %}
        {% for block in event.calendar.level(1).all() %}
            {% switch block.type.handle %}
                {% case 'periodOfDays' %}
                    {% if block.endDate|date('d/m/Y') > today  %}
                        {% if block.startDate|date('d/m/Y') < today %}
                            {# Ongoing event #}
                            {% set futureEvents = futureEvents|merge([{'date': today, 'event': event}]) %}
                        {% else %}
                            {# Future event #}
                            {% set futureEvents = futureEvents|merge([{'date': block.startDate|date('d/m/Y'), 'event': event}]) %}
                        {% endif %}
                    {% endif %}
                {% case 'specificDates' %}
                    {% set tempEvent = [] %}
                    {% set break = false %}
                    {% set empty = false %}
                    {% for child in block.children.all() if not break %}
                        {% if child.dateAndTime|date('U') > now %}
                            {# Future event #}
                            {% set tempEvent = [{'date': child.dateAndTime|date('d/m/Y'), 'event': event}] %}
                            {% set break = true %}
                            {# Break because event is found #}
                        {% elseif (child.dateAndTime|date('U') + child.duration*60) > now %}
                            {# Ongoing event #}
                            {% set tempEvent = [{'date': today, 'event': event}] %}
                            {% set break = true %}
                            {# Break because event is found #}
                        {% endif %}
                        {% else %}
                            {% set empty = true %}
                    {% endfor %}
                    {% if tempEvent is same as([]) and empty %}
                        {# Fallback on default #}
                    {% elseif tempEvent is not same as([]) %}
                        {% set futureEvents = futureEvents|merge(tempEvent) %}
                    {% endif %}
            {% endswitch %}
        {% else %}
            {% if event.dateAndTime|date('U') > now %}
                {# Future event #}
                {% set futureEvents = futureEvents|merge([{'date': event.dateAndTime|date('d/m/Y'), 'event': event}]) %}
            {% elseif (event.dateAndTime|date('U') + event.duration*60) > now %}
                {# Ongoing event #}
                {% set futureEvents = futureEvents|merge([{'date': today, 'event': event}]) %}
            {% endif %}
        {% endfor %}
    {% endfor %}
    {% set orderedFutureEvents = [] %}
    {% for key in futureEvents|sort((a, b) => a <=> b)|column('date', 'event')|keys %}
        {% set orderedFutureEvents = orderedFutureEvents|merge([futureEvents[key].event]) %}
    {% endfor %}
    {{ orderedFutureEvents|json_encode|raw }}
{% endmacro %}

**Tags:** json, yii, macro, twigextension

> **Comments:**
> **username_1**: That last bit looks wrong: })())); I think you need to fix the paranthesis.  
> **username_2**: Just trying to follow along... `orderedFutureEvents` is an array of entries that you JSON encode into a string.  Is `events` in your macro the same JSON encoded `orderedFutureEvents` that gets passed into the macro?  
> **username_0**: @BradBell the `events` parameter is an array of entry objects. The macro filters out specific entries to return a new array of entry objects. I'll update my post to make it a bit more clear.  

---

## 2 answers

### Answer by username_3 on 2021-11-30. Score: 2

After some local experimentation I managed to reproduce and (hopefully) resolve.
Like Brad said the macro returns a Markup class that is not json decodable.
So first step is to use the raw filter to get the raw string (alternatively, you can call the jsonSerialize method on the Markup class).
But then I ran into another issue, where double quotes were replaced with ". This caused a syntax error when trying to decode. A replace filter was the only way I could get rid of those.
Another apparent issue is that you are trying to echo an array, which will throw an Array to string conversion exception, even when JSON decode is successful.
So I'd replace:
{% set futureEvents %}
    {{ eventsMacro.futureEvents(events)|json_decode }}
{% endset %}

With:
{% set futureEvents = eventsMacro.futureEvents(events) | raw | replace('"','"') | json_decode %}


---

### Answer by username_2 on 2020-10-26. Score: 0

Guessing if you change your macro to this, it'll work:
{{ orderedFutureEvents|json_encode|raw }}

The reason being that Twig will return a Markup class that wraps the JSON content when returning from the macro that json_encode doesn't know what to do with.
Passing the contents through |raw returns the original JSON.

> **Comments:**
> **username_0**: Unfortunately, adding the raw tag to the macro variable didn't solve the issue. When I dump what the macro returned, it's still a Markup object. `object(Twig\Markup)#8733 (2) { ["content":"Twig\Markup":private]=> string(22297) " [{"sectionId":"5","typeId":"5", ... `  
> **username_2**: Can you share the full macro?  
> **username_0**: I've added the full macro to the question. (I know it's a really bad way to handle this kind of stuff)  
> **username_2**: Hrm... I'm out of ideas. Are you comfortable with xDebug and PHPStorm? Could debug the compiled templates to get some additional insights.  

---

