<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>posts on @prashantv</title>
    <link>https://prashantv.com/posts/</link>
    <description>Recent content in posts on @prashantv</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>en</language>
    <lastBuildDate>Thu, 20 Jun 2024 18:00:00 +0800</lastBuildDate><atom:link href="https://prashantv.com/posts/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Roundtrip JSON through a partial Go struct</title>
      <link>https://prashantv.com/posts/json-partial-go/</link>
      <pubDate>Thu, 20 Jun 2024 18:00:00 +0800</pubDate>
      
      <guid>https://prashantv.com/posts/json-partial-go/</guid>
      <description>Retain unknown fields when marshalling a Go struct</description>
      <content:encoded><![CDATA[<p>Go&rsquo;s standard library json package makes it easy
to unmarshal JSON into a correspondingly typed struct, and vice-versa.
By default, <code>json.Unmarshal</code> ignores any fields in the JSON
that don’t match a struct field,
and <code>json.Marshal</code> will only marshal fields on the struct.
To fully roundtrip JSON from unmarshal to marshal,
the Go struct will need to represent the full schema of the JSON data.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Page</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Slug</span>  <span class="kt">string</span> <span class="s">`json:&#34;slug&#34;`</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">input</span> <span class="o">:=</span> <span class="s">`{&#34;title&#34;:&#34;Contact Us&#34;,&#34;slug&#34;:&#34;contact&#34;,&#34;icon&#34;:&#34;email&#34;}`</span>
</span></span><span class="line"><span class="cl"><span class="kd">var</span> <span class="nx">p</span> <span class="nx">Page</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">input</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">p</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">output</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">output</span><span class="p">))</span> <span class="c1">// drops the &#34;icon field.
</span></span></span><span class="line"><span class="cl"><span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1">// {&#34;title&#34;:&#34;Contact Us&#34;,&#34;slug&#34;:&#34;contact&#34;}
</span></span></span></code></pre></div><p>To include the icon field in the output,
it needs to be added to the <code>Page</code> struct:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Page</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Slug</span>  <span class="kt">string</span> <span class="s">`json:&#34;name&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Icon</span>  <span class="kt">string</span> <span class="s">`json:&#34;icon&#34;`</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="o">...</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1">// {&#34;title&#34;:&#34;Contact Us&#34;,&#34;slug&#34;:&#34;contact&#34;,&#34;icon&#34;:&#34;email&#34;}
</span></span></span></code></pre></div><p>This approach is reasonable when there&rsquo;s a small number of fields,
but it gets unwieldy for larger, complex JSON payloads.
If the Go code only needs to interact with a small subset of fields,
ideally only those would be defined in the struct.
This would make it easier to deal with schema changes,
as only changes impacting the fields used in Go
require a code change.</p>
<h3 id="storing-unknown-fields-in-a-mapstringany">Storing unknown fields in a <code>map[string]any</code></h3>
<p>Go can unmarshal JSON without a schema into a <code>map[string]any</code>,
which can be used to retain the original fields.
We can unmarshal the input twice:
first into a typed struct for fields that we need to interact with in Go
and again into a <code>map[string]any</code> to retain all other fields.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Page</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Slug</span> <span class="kt">string</span> <span class="s">`json:&#34;slug&#34;`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">raw</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">any</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Page</span><span class="p">)</span> <span class="nf">UnmarshalJSON</span><span class="p">(</span><span class="nx">data</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">p</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>However, this won&rsquo;t work since <code>json.Unmarshal(data, p)</code>
will end up calling the same <code>UnmarshalJSON</code> function.
We&rsquo;ve inadvertently recursed into ourself, causing a stack overflow!</p>
<p>There is a simple workaround: we can create a new type,
which inherits all the same fields,
and can be casted to our original type.
But it won&rsquo;t inherit the <code>UnmarshalJSON</code> method
so it&rsquo;ll use the default behaviour of unmarshalling
into the exported fields:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Page</span><span class="p">)</span> <span class="nf">UnmarshalJSON</span><span class="p">(</span><span class="nx">data</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">type</span> <span class="nx">pageNoJSON</span> <span class="nx">Page</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">copy</span> <span class="nx">pageNoJSON</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">copy</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="o">*</span><span class="nx">p</span> <span class="p">=</span> <span class="nf">Page</span><span class="p">(</span><span class="nx">copy</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This approach keeps the unknown fields in <code>Page.raw</code>,
but it doesn&rsquo;t use them for marshalling.
We can&rsquo;t marshal the map as-is,
as that wouldn&rsquo;t reflect changes made to the fields in <code>Page</code>.
Instead, we can replace the values in the map
with the field values as part of <code>MarshalJSON</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">Page</span><span class="p">)</span> <span class="nf">MarshalJSON</span><span class="p">()</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">[</span><span class="s">&#34;title&#34;</span><span class="p">]</span> <span class="p">=</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Title</span>
</span></span><span class="line"><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">[</span><span class="s">&#34;slug&#34;</span><span class="p">]</span> <span class="p">=</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Slug</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>This achieves our original goal
of only defining a schema
for fields that the Go code interacts with.
All fields are retained from unmarshal to marshal
while allowing the struct to be modified:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Page</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Slug</span>  <span class="kt">string</span> <span class="s">`json:&#34;slug&#34;`</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">raw</span> <span class="kd">map</span><span class="p">[</span><span class="kt">string</span><span class="p">]</span><span class="nx">any</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Page</span><span class="p">)</span> <span class="nf">UnmarshalJSON</span><span class="p">(</span><span class="nx">data</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="kd">type</span> <span class="nx">pageNoJSON</span> <span class="nx">Page</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">copy</span> <span class="nx">pageNoJSON</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">copy</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">	<span class="o">*</span><span class="nx">p</span> <span class="p">=</span> <span class="nf">Page</span><span class="p">(</span><span class="nx">copy</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="o">&amp;</span><span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="kc">nil</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">Page</span><span class="p">)</span> <span class="nf">MarshalJSON</span><span class="p">()</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">[</span><span class="s">&#34;title&#34;</span><span class="p">]</span> <span class="p">=</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Title</span>
</span></span><span class="line"><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">[</span><span class="s">&#34;slug&#34;</span><span class="p">]</span> <span class="p">=</span> <span class="nx">p</span><span class="p">.</span><span class="nx">Slug</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="nf">example</span><span class="p">()</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">input</span> <span class="o">:=</span> <span class="s">`{&#34;title&#34;:&#34;Contact Us&#34;,&#34;slug&#34;:&#34;contact&#34;,&#34;icon&#34;:&#34;email&#34;}`</span>
</span></span><span class="line"><span class="cl">	<span class="kd">var</span> <span class="nx">p</span> <span class="nx">Page</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Unmarshal</span><span class="p">([]</span><span class="nb">byte</span><span class="p">(</span><span class="nx">input</span><span class="p">),</span> <span class="o">&amp;</span><span class="nx">p</span><span class="p">);</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">p</span><span class="p">.</span><span class="nx">Slug</span> <span class="p">=</span> <span class="s">&#34;contact-us&#34;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">output</span><span class="p">,</span> <span class="nx">err</span> <span class="o">:=</span> <span class="nx">json</span><span class="p">.</span><span class="nf">Marshal</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">	<span class="k">if</span> <span class="nx">err</span> <span class="o">!=</span> <span class="kc">nil</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="k">return</span> <span class="nx">err</span>
</span></span><span class="line"><span class="cl">	<span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">fmt</span><span class="p">.</span><span class="nf">Println</span><span class="p">(</span><span class="nb">string</span><span class="p">(</span><span class="nx">output</span><span class="p">))</span> <span class="c1">// retains &#34;icon&#34;, and has updated &#34;slug&#34;
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// Output:
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>	<span class="c1">// {&#34;icon&#34;:&#34;email&#34;,&#34;slug&#34;:&#34;contact-us&#34;,&#34;title&#34;:&#34;Contact Us&#34;}
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="p">}</span>
</span></span></code></pre></div><p>This approach requires a lot of per-type noise
as every nested type will need the same
<code>UnmarshalJSON</code> and <code>MarshalJSON</code> methods.
The <code>UnmarshalJSON</code> looks similar for all types,
while the <code>MarshalJSON</code> requires duplicating the fields
defined in the schema.</p>
<p>Each type will need <code>UnmarshalJSON</code> and <code>MarshalJSON</code>
to override the default marshalling behavior
but the implements can be simplified with <code>reflect</code>.</p>
<h3 id="simplifying-integration-with-jsonobj">Simplifying integration with <a href="https://pkg.go.dev/github.com/prashantv/pkg/jsonobj">jsonobj</a></h3>
<p><a href="https://pkg.go.dev/github.com/prashantv/pkg/jsonobj">jsonobj</a> builds on the above ideas
but simplifies integration for each type
by minimizing type code
and avoiding fields duplicated in marshalling.</p>
<p>For each type, integrating requires:</p>
<ul>
<li>An unexported field, <code>raw jsonobj.Retain</code></li>
<li><code>UnmarshalJSON</code> method that calls <code>raw.UnmarshalJSON(data, obj)</code></li>
<li><code>MarshalJSON</code> method that returns <code>raw.ToJSON(obj)</code></li>
</ul>
<p>The above example is simplified to:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-go" data-lang="go"><span class="line"><span class="cl"><span class="kd">type</span> <span class="nx">Page</span> <span class="kd">struct</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="nx">raw</span> <span class="nx">jsonobj</span><span class="p">.</span><span class="nx">Retain</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="nx">Title</span> <span class="kt">string</span> <span class="s">`json:&#34;title&#34;`</span>
</span></span><span class="line"><span class="cl">	<span class="nx">Slug</span>  <span class="kt">string</span> <span class="s">`json:&#34;slug&#34;`</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="o">*</span><span class="nx">Page</span><span class="p">)</span> <span class="nf">UnmarshalJSON</span><span class="p">(</span><span class="nx">data</span> <span class="p">[]</span><span class="kt">byte</span><span class="p">)</span> <span class="kt">error</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">.</span><span class="nf">FromJSON</span><span class="p">(</span><span class="nx">data</span><span class="p">,</span> <span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">func</span> <span class="p">(</span><span class="nx">p</span> <span class="nx">Page</span><span class="p">)</span> <span class="nf">MarshalJSON</span><span class="p">()</span> <span class="p">([]</span><span class="kt">byte</span><span class="p">,</span> <span class="kt">error</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	<span class="k">return</span> <span class="nx">p</span><span class="p">.</span><span class="nx">raw</span><span class="p">.</span><span class="nf">ToJSON</span><span class="p">(</span><span class="nx">p</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>The behaviour stays the same:
unknown fields are retained,
and changes to struct fields are reflected
in the marshalled JSON.</p>
<p>For more details, check out the package documentation
for <a href="https://pkg.go.dev/github.com/prashantv/pkg/jsonobj">jsonobj</a>.</p>
<h3 id="json-v2-in-the-standard-library">JSON v2 in the standard library</h3>
<p>A new json v2 package in the standard library
is <a href="https://github.com/golang/go/discussions/63397">planned</a>
and will include support for unknown fields,
simplifying the integration once it&rsquo;s released!</p>
<h3 id="related">Related</h3>
<ul>
<li><a href="https://github.com/golang/go/issues/22533">go issue: proposal: encoding/json: preserve unknown fields</a></li>
<li><a href="https://github.com/golang/go/discussions/63397">go discussion: encoding/json/v2</a></li>
<li><a href="https://www.reddit.com/r/golang/comments/q4gyr9/unmarshal_some_fields_in_struct_and_the_rest_in/">reddit: Unmarshal some fields in struct and the rest in map</a></li>
<li><a href="https://stackoverflow.com/questions/33436730/unmarshal-json-with-some-known-and-some-unknown-field-names">stack overflow: Unmarshal JSON with some known, and some unknown field names</a></li>
<li><a href="https://pkg.go.dev/github.com/prashantv/pkg/jsonobj">jsonobj</a></li>
</ul>
]]></content:encoded>
    </item>
    
  </channel>
</rss>
