Unmarshalling stream of changing types

agolangf · · 439 次点击    
这是一个分享于 的资源,其中的信息可能已经有所发展或是发生改变。
<p>I&#39;m currently dealing with a stream of json objects coming in to my application, and am having some difficulties figuring out what the best way of parsing them is. The stream consists of objects that have a defined type. The problem is that one of the fields in the object is of changing type. It looks like this:</p> <pre><code>[{ &#34;status&#34;: &#34;closed&#34;, &#34;type&#34;: &#34;transaction&#34;, &#34;transaction&#34;: { &#34;TransactionType&#34;: &#34;TypeA&#34;, &#34;Account&#34;: &#34;Some string&#34;, &#34;Fee&#34;: &#34;14&#34;, &#34;date&#34;: 45325680 }, &#34;validated&#34;: true }, { &#34;status&#34;: &#34;closed&#34;, &#34;type&#34;: &#34;transaction&#34;, &#34;transaction&#34;: { &#34;TransactionType&#34; : &#34;TypeB&#34;, &#34;Account&#34; : &#34;Some string&#34;, &#34;Fee&#34;: &#34;42&#34;, &#34;Destination&#34; : &#34;Some string&#34; }, &#34;validated&#34;: true }] </code></pre> <p>You can see that the &#34;parent&#34; does not change, but the &#34;transaction&#34; does. I removed a lot of fields from the &#34;transaction&#34; field to make it easier to explain, but this is a type with 10-ish common fields and 10-ish changing fields that are dependent on the type. There are also 10 transaction types, this makes it pretty annoying to put everything into one struct and have a ton of optional fields. I was also thinking about one struct for every transaction type, but this does not work because then there is no way of specifying which type the field should have in the parent.</p> <p>How would I parse these objects effectively? Unfortunately, I can&#39;t change the structure of the elements coming out of the stream. What would be the best way to solve this?</p> <p>EDIT: Thank you guys all for your help, I have learned some new things about go, but I have found a solution. I realized that I didn&#39;t need to keep the parent-child relation in my data, so what I did is: Unmarshal only the TransactionType -&gt; Use this to unmarshal the Transaction -&gt; Unmarshal the parent and add it to the Transaction in a new field. This way each transaction has a different type while keeping all data intact. It seems to me that this is a fairly okay-ish solution with no weird stuff going on, so this is the solution I settled for. Thanks again!</p> <hr/>**评论:**<br/><br/>TAAnderson: <pre><p>See: <a href="http://eagain.net/articles/go-dynamic-json/" rel="nofollow">http://eagain.net/articles/go-dynamic-json/</a></p></pre>martijn9612: <pre><p>Ah that does make sense, the only thing that is unclear to me then is the final product. Since there will be multiple &#34;Sound&#34; structs which all reside in a encapsulating struct, what type should I give the field where the &#34;Sound&#34;s will be in? That is the only thing unclear to me.</p></pre>jerf: <pre><p>I <em>think</em> the answer to your question is the JSON.RawMessage type, but I&#39;m not quite sure I&#39;m navigating the shift between the blog post&#39;s examples and yours correctly. If you put it into a Playground example we may be able to be more clear. (Even if it starts as a non-functional one.)</p></pre>abdullah2993: <pre><p>write a custom JSON Unmarshaler? Or use RawMessage type to further expand according to type. Here is an example: <a href="https://play.golang.org/p/XiYNLRgIl_" rel="nofollow">https://play.golang.org/p/XiYNLRgIl_</a></p> <p>You should read the docs for encoding/json</p></pre>martijn9612: <pre><p>I have been thinking about both, the the problem that still isn&#39;t solved there is the fact that the parent struct wont like having a field that is a struct with TypeA or TypeB. Is there a way to solve this?</p> <p>I know how encoding/json works, but the structure you linked here does not work since the type declaration is only known after unmarshalling the &#34;transaction&#34; part. I could use a contains on the RawMessage, this allows me to unmarshal to the correct type, but I won&#39;t be able to set the struct in the correct field since the type may differ right?</p></pre>tcrypt: <pre><p>Would something like this work? <a href="https://play.golang.org/p/f1RCSK18-Y" rel="nofollow">https://play.golang.org/p/f1RCSK18-Y</a></p> <p>Edit: If you really needed the unmarshalled inner transactions set in the parent object, you could just have a field for each type in the parent and only set the one that gets unmarshalled.</p></pre>tcrypt: <pre><p>Or if the logic to process each message type could be contained in a <code>Process()</code> method on each type then you could have an interface for it and the parent type could store it.</p></pre>hobbified: <pre><p>Why not? Just give the field an interface type which all of the transaction types satisfy.</p></pre>Emacs24: <pre><p>The only way to be able to parse the stream effectively without having large structure is a guarantee if <code>TransactionType</code> will always be the first field and custom parser based on this fact.</p> <p>Another approach is to switch from </p> <p><code> &#34;transaction&#34;: { </code></p> <p>to</p> <p><code> &#34;transactionA&#34;: { </code></p> <p>You also can consider switching to protobuf with its <code>oneof</code> to have something like</p> <pre><code>oneof transaction { TransactionTypeA transactionA = 1; TransactionTypeB transactionB = 2; … } </code></pre> <p>All in all, the JSON is a poor serialization format for such kind of data.</p></pre>

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

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