github.com/ucarion/sfv
is a Golang implementation of Structured Field
Values, aka RFC 8941. You can use
sfv
to encode and decode data in well-formatted HTTP headers. This package is
fully compliant with the standard SFV test
suite.
You can install this package by running:
go get github.com/ucarion/sfv
The cleverness of the Structured Field Values specification is that it
retroactively makes sense of a lot of existing HTTP headers. For instance, the
Content-Type
header happens to be a well-formed SFV header:
Content-Type: text/html; charset=utf-8
Here's a struct you can use with sfv
to read and write this sort of data:
type ContentType struct {
MediaType string
Charset string `sfv:"charset"`
}
So now you can parse Content-Type
headers:
var contentType ContentType
if err := sfv.Unmarshal("text/html; charset=utf-8", &contentType); err != nil {
panic(err)
}
fmt.Println(contentType.MediaType) // Outputs: text/html
fmt.Println(contentType.Charset) // Outputs: utf-8
Or write them out:
contentType := ContentType{MediaType: "text/html", Charset: "utf-8"}
out, err := sfv.Marshal(contentType)
fmt.Println(err) // Outputs: <nil>
fmt.Println(out) // Outputs: text/html;charset=utf-8
The online reference documentation has dozens of examples of how you can convert SFV items, lists, or dictionaries to/from their Golang equivalents.
When you use sfv
with custom types, as shown in the example above, you may
lose some information. For instance, sfv.Unmarshal
will ignore parameters that
aren't specified in your struct, and will not preserve the order of parameters
or dictionaries.
If you need to round-trip data exactly, you can use sfv.Marshal
or
sfv.Unmarshal
with the sfv.Item
, sfv.List
, and sfv.Dictionary
types.
These types are treated specially by sfv
, and guarantee that no data (except
for things like extra whitespace) will be lost in the process of
serializing/deserializing data.
For instance:
var dict sfv.Dictionary
sfv.Unmarshal("a=1,c=3,b=2", &dict)
// You can use sfv.Dictionary to iterate over keys in the order they appeared in
// the input.
fmt.Println(dict.Keys) // Outputs: [a c b]
// If dict were a map[string]int, the order of keys in this output would not be
// guaranteed.
fmt.Println(sfv.Marshal(dict)) // Outputs: a=1,c=3,b=2 <nil>