实现使用 JSON 的 io.WriterTo 接口。

huangapple go评论72阅读模式
英文:

Implement io.WriterTo with JSON

问题

我最近发现了这个很酷的接口,io.WriterTo

https://godocs.io/io#WriterTo

我想为一些 JSON 对象实现它。我已经能够做到这一点:

package calendar

import (
   "bytes"
   "encoding/json"
   "io"
)

type date struct {
   Month int
   Day int
}

func (d date) WriteTo(w io.Writer) (int64, error) {
   buf := new(bytes.Buffer)
   err := json.NewEncoder(buf).Encode(d)
   if err != nil {
      return 0, err
   }
   return buf.WriteTo(w)
}

但我认为这不是理想的,因为它在发送给 Writer 之前会在内存中复制对象。有没有可能直接写入,同时知道写入了多少字节?

英文:

I found this cool interface recently, io.WriterTo:

https://godocs.io/io#WriterTo

I would like to implement it for some JSON objects. I was able to make this:

package calendar

import (
   "bytes"
   "encoding/json"
   "io"
)

type date struct {
   Month int
   Day int
}

func (d date) WriteTo(w io.Writer) (int64, error) {
   buf := new(bytes.Buffer)
   err := json.NewEncoder(buf).Encode(d)
   if err != nil {
      return 0, err
   }
   return buf.WriteTo(w)
}

But I think it's not ideal, as it makes a copy of the object in memory, before sending to the Writer. Is it possible to write directly, but also know how many bytes were written?

答案1

得分: 2

直接编写一个io.Writer包装器来计算写入的字节数:

type countingWriter struct {
    n int64
    w io.Writer
}

func (cw *countingWriter) Write(p []byte) (int, error) {
    n, err := cw.w.Write(p)
    cw.n += int64(n)
    return n, err
}

将WriteTo方法更改为将数据编码到一个包装了参数的写入器中。完成后返回字节数和错误。

func (d date) WriteTo(w io.Writer) (int64, error) {
    cw := &countingWriter{w: w}
    err := json.NewEncoder(cw).Encode(d)
    return cw.n, err
}

在Go PlayGround上运行示例

英文:

To write directly, create an io.Writer wrapper that counts the bytes written:

type countingWriter struct {
	n int64
	w io.Writer
}

func (cw *countingWriter) Write(p []byte) (int, error) {
	n, err := cw.w.Write(p)
	cw.n += int64(n)
	return n, err
}

Change the WriteTo method to encode to a writer where the writer is the wrapper on the argument. Return the count of bytes and error when done.

func (d date) WriteTo(w io.Writer) (int64, error) {
	cw := &countingWriter{w: w}
	err := json.NewEncoder(cw).Encode(d)
	return cw.n, err
}

Run an example on the the Go PlayGround

答案2

得分: 0

是否可以直接写入,并且知道写入了多少字节呢?

巧合的是,这正是《Go程序设计语言》一书中的练习7.2所涉及的内容。该练习要求实现一个具有以下签名的函数:

func CountingWriter(w io.Writer) (io.Writer, *int64)

返回的int64指针必须始终包含写入返回的io.Writer的字节数。本质上,返回的io.Writer是一个装饰器,它通过更新字节计数器来增强初始io.Writer的功能。

首先,让我们创建类型writerFunc,它具有与io.Writer的唯一方法Write相同的参数化:

type writerFunc func([]byte) (int, error)

然后,在writerFunc上定义Write(p []byte) (int, error)方法:

func (wf writerFunc) Write(p []byte) (int, error) {
    return wf(p)
}

这样,writerFunc满足io.Writer并且可以将任何func([]byte) (int, error)适配为io.Writer - 也就是说,我们可以在需要io.Writer的地方将func([]byte) (int, error)包装在writerFunc中。

最后,我们来看一下我们要找的CountingWriter装饰函数:

func CountingWriter(w io.Writer) (io.Writer, *int64) {
    count := new(int64)
    writeAndCount := func(data []byte) (int, error) {
        bytes, err := w.Write(data)
        *count += int64(bytes)
        return bytes, err
    }
    return writerFunc(writeAndCount), count
}

请注意最后的return语句:闭包writeAndCount被包装在writerFunc中。这是因为闭包的类型也是func([]byte) (int, error)。正如我们上面所看到的,writerFunc满足io.Writer,这样调用此函数的调用者最终就会得到它。

英文:

> Is it possible to write directly, but also know how many bytes were written?

Coincidentally, it is what exercise 7.2 of the book The Go Programming Language is about. The exercise consists of implementing a function with the signature:

func CountingWriter(w io.Writer) (io.Writer, *int64)

The returned pointer to int64 must contain at any moment the number of bytes written to the returned io.Writer. Essentially, the returned io.Writer is a decorator as it enhances the functionality of the initial io.Writer by updating a byte counter.

First, let's create the type writerFunc, which has the same parametrization as the only method of io.Writer, Write:

type writerFunc func([]byte) (int, error)

Then, define the method Write(p []byte) (int, error) on writerFunc:

func (wf writerFunc) Write(p []byte) (int, error) {
	return wf(p)
}

This way, writerFunc satisfies io.Writer and serves as an adapter for any func([]byte) (int, error) into an io.Writer – i.e., we can wrap a func([]byte) (int, error) in a writerFunc whenever an io.Writer is required.

Finally, the CountingWriter decorating function we are looking for:

func CountingWriter(w io.Writer) (io.Writer, *int64) {
	count := new(int64)
	writeAndCount := func(data []byte) (int, error) {
		bytes, err := w.Write(data)
		*count += int64(bytes)
		return bytes, err
	}
	return writerFunc(writeAndCount), count
}

Note the last return statement: the closure writeAndCount is wrapped in a writerFunc. This works because the closure's type is func([]byte) (int, error) as well. As we have seen above, writerFunc satisfies io.Writer, which the caller of this function eventually receives.

huangapple
  • 本文由 发表于 2022年6月15日 06:57:02
  • 转载请务必保留本文链接:https://go.coder-hub.com/72624224.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定