Adding fields when marshalling JSON

polaris · · 570 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I know that this is possible when you are dealing with defined structs, using structures that wrap other structures. However, I have a function that is returning an <code>interface{}</code>. All I know about the <code>interface{}</code> is that it is JSON encodable. I would like to add a timestamp field to the output when I Marshal the object.</p> <pre><code>package main import &#34;encoding/json&#34; import &#34;fmt&#34; func Encode(data interface{}) { output, _ := json.Marshal(struct{ Data interface{} Timestamp int `json:&#34;ts&#34;` }{data, 3}) fmt.Println(string(output)) } func main() { x := map[string]int{&#34;arbitrary&#34;:6} // I want this to print {&#34;arbitrary&#34;: 6, &#34;ts&#34;: 3} Encode(x) // It actually prints {&#34;Data&#34;:{&#34;arbitrary&#34;:6}, &#34;ts&#34;:3} } </code></pre> <p>Is there some way to get rid of the &#34;Data&#34; key and have the &#34;ts&#34; key be at the same level as the fields in data?</p> <hr/>**评论:**<br/><br/>binaryblade: <pre><p>Because that interface could be an array or otherwise it may be tricky. Although you can try embedding the interface instead.</p></pre>TheMerovius: <pre><p>Embedding won&#39;t help. The <a href="https://godoc.org/encoding/json#Marshal" rel="nofollow">encoding rules</a> state that embedded interface types are always keyed.</p></pre>whizack: <pre><p>If you changed &#39;data&#39; to a map[string]interface{} then you could append your own key to the data before you encode it.</p></pre>Eggbert345: <pre><p>Yeah I thought of that, except the data I&#39;ll be dealing with will be all different structs. And I don&#39;t want to reflect over the struct fields to pull them into a new map[string]interface{}, that just seems like doing double duty with <code>encoding/json</code> and not really performant.</p></pre>whizack: <pre><p>if you can guarantee they are all structures (and not just raw slices or values), you could do something like this: <a href="https://play.golang.org/p/AXoh1fQYyp" rel="nofollow">https://play.golang.org/p/AXoh1fQYyp</a></p></pre>TheMerovius: <pre><p>I don&#39;t think there is any way to reliably do that, except with reflection. You could try something like <code>str = &#34;{\&#34;timestamp\&#34;: 123456789,&#34; + str[1:]</code> with <code>str</code> being the json encoding of the value. But that would break if the encoded value itself contains a field <code>timestamp</code>. Or if the encoded value is an array, instead of an object (or even a string, number,boolean…).</p></pre>TheMerovius: <pre><p>Another way would be to encode, decode into map[string]interface{}, adding timestamp, encode.</p></pre>xargon7: <pre><p>You should check the encoded result to see if it marshals to a JSON object. Reflection isn&#39;t likely to give you the right result because the underlying object could implement JSONMarshaller even if it&#39;s a int.</p> <p>Something like this: <a href="http://play.golang.org/p/ZT-O2h4zfo" rel="nofollow">http://play.golang.org/p/ZT-O2h4zfo</a></p></pre>TheMerovius: <pre><blockquote> <p>Reflection isn&#39;t likely to give you the right result because the underlying object could implement JSONMarshaller even if it&#39;s a int.</p> </blockquote> <p>The json-package only uses reflection itself. So I disagree with this statement. In fact, the simplest and most robust way to do what OP wants is probably to copy-paste the relevant parts of json and then add the code in there directly.</p></pre>xargon7: <pre><blockquote> <p>The json-package only uses reflection itself. So I disagree with this statement.</p> </blockquote> <p>Let me clarify: Using only reflection gives you no guarantees on whether the resulting encoded data corresponds to a JSON object. You have to <em>actually marshal</em> the data (in the general case) and inspect the result -- which is exactly what you were saying: </p> <blockquote> <p>In fact, the simplest and most robust way to do what OP wants is probably to copy-paste the relevant parts of json and then add the code in there directly.</p> </blockquote> <p>Agreed -- that what I did in my snippet above.</p></pre>Eggbert345: <pre><p>I see what you both are saying. However I know that the data will be a struct with JSON tags, I just don&#39;t know which one. I was hoping there would be a solution that wouldn&#39;t require me to copy the JSON library.</p></pre>xargon7: <pre><p>Take a look at the link I posted -- it&#39;s very efficient, simple, and generic, and you can explicitly handle a non-struct JSON result however you like.</p></pre>xargon7: <pre><p>By the way, an important caveat: If the structure that you are marshaling already has a &#34;ts&#34; field, then you will end up with two &#34;ts&#34; fields in the generated struct. This is valid JSON, but the Go code won&#39;t know what to do with it and will probably corrupt your data when you decode it. Most language libraries have inconsistent handling of repeated keys in JSON data.</p></pre>izuriel: <pre><p>I know this off topic would it seem,given that JSON is a subset of JavaScript, that there should be a very clear way of handling duplicate keys -- the last value applies. </p></pre>thepciet: <pre><p>JSON objects are not maps! Duplicate names (keys) are a feature.</p> <blockquote> <p>An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).</p> </blockquote> <p><a href="http://www.json.org/" rel="nofollow">http://www.json.org/</a></p></pre>izuriel: <pre><p>I was getting at this part of the person I replied to:</p> <blockquote> <p>... Most language libraries have inconsistent handling of repeated keys in JSON data.</p> </blockquote> <p>Because in my experience it&#39;s never really been a problem. Let me just try it real quick...</p> <p>Ruby handles it gracefully... the last value.</p> <pre><code>&gt;&gt; require &#34;json&#34; =&gt; true &gt;&gt; JSON.parse(&#39;{&#34;one&#34;: 1, &#34;two&#34;: 2, &#34;one&#34;: 3}&#39;) =&gt; {&#34;one&#34;=&gt;3, &#34;two&#34;=&gt;2} </code></pre> <p>Python handles it as expected...</p> <pre><code>&gt;&gt;&gt; import json &gt;&gt;&gt; json.loads(&#39;{&#34;one&#34;: 1, &#34;two&#34;: 2, &#34;one&#34;: 3}&#39;) {u&#39;two&#39;: 2, u&#39;one&#39;: 3} </code></pre> <p>Node (or JavaScript) handles it as expected...</p> <pre><code>&gt; JSON.parse(&#39;{&#34;one&#34;: 1, &#34;two&#34;: 2, &#34;one&#34;: 3}&#39;) { one: 3, two: 2 } </code></pre> <p>And of course Go gracefully handles it as would be expected... <a href="http://play.golang.org/p/h_Fjd-zjp5" rel="nofollow">http://play.golang.org/p/h_Fjd-zjp5</a> (both with a map and with a struct).</p> <p>So it&#39;s as a I thought, duplicate keys are no worries. Just now that the most recent value associated to a key is the one that matters.</p></pre>

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

570 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传