Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-11-30T12:33:25.757Z
Answers.LastActivityDate: 2021-11-30T13:04:30.447Z
Answers.LastEditDate: 2021-11-30T13:04:30.447Z
Answers.UserDisplayName: Matanya
Answers.Body: <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>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-10-26T21:03:06.353Z
Answers.Text: 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", ... `
Answers.UserDisplayName: Joorren
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-10-27T02:16:12.867Z
Answers.Text: Can you share the full macro?
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-10-28T10:10:21.887Z
Answers.Text: I've added the full macro to the question. (I know it's a really bad way to handle this kind of stuff)
Answers.UserDisplayName: Joorren
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-10-28T20:14:36.730Z
Answers.Text: Hrm... I'm out of ideas. Are you comfortable with xDebug and PHPStorm? Could debug the compiled templates to get some additional insights.
Answers.UserDisplayName: Brad Bell
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2020-10-26T16:51:02.723Z
Answers.LastActivityDate: 2020-10-26T16:51:02.723Z
Answers.UserDisplayName: Brad Bell
Body: <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>
  
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2020-10-23T20:48:40.353Z
Comments.Text: That last bit looks wrong: })())); I think you need to fix the paranthesis.
Comments.UserDisplayName: CraftN00b
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2020-10-26T03:13:46.640Z
Comments.Text: 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?
Comments.UserDisplayName: Brad Bell
Comments.ContentLicense: CC BY-SA 4.0
Comments.CreationDate: 2020-10-26T14:36:05.527Z
Comments.Text: @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.
Comments.UserDisplayName: Joorren
Tags: <json><yii><macro><twigextension>
Title: Twig json_decode gives Syntax Error
ThreadText: # Twig json_decode gives Syntax Error
  
  (Asked by: username_0 on 2020-10-23)
  
  I'm trying to use the [json\_decode](https://craftcms.com/docs/3.x/dev/filters.html#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:
  
  ```php
  {% 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:
  
  ```php
  $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:
  
  ```php
  {% 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**: @username_2 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 username_2 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 `&quot;`. 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('&quot;','"') | 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](https://github.com/twigphp/Twig/blob/3.x/src/Markup.php) 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.  
  
  ---
  
  