Documentation ¶
Overview ¶
Package jsonfs provides a JSON marshal/unmarshaller that treats JSON objects as directories and JSON values (bools, numbers or strings) as files.
This is an alternative to various other structures for dealing with JSON that either use maps or structs to represent data. This is particularly great for doing discovery on JSON data or manipulating JSON.
Each file is read-only. You can switch out files in a directory in order to make updates.
A File represents a JSON basic value of bool, integer, float, null or string.
A Directory represents a JSON object or array. Because a directory can be an object or array, a directory that represents an array has _.array._ appened to the name in some filesystems. If creating an array using filesytem tools, use ArrayDirName("name_of_your_array"), which will append the correct suffix. You always opened the file with simply the name. This also means that naming an object (not array) _.array._ will cause unexpected results.
This is a thought experiment. However, it is quite performant, but does have the unattractive nature of being more verbose. Also, you may find problems if your JSON dict keys have invalid characters for the filesystem you are running on AND you use the diskfs filesystem. For the memfs, this is mostly not a problem, except for /. You cannot use / in your keys.
Benchmarks ¶
We are slower and allocate more memory. I'm going to have to spend some time optimising the memory use. I had some previous benchmarks that showed this was faster. But I had a mistake that became obvious with using a large file, (unless I was 700,000x faster on unmarshal, and I'm not that good).
BenchmarkUnmarshalSmall-10 132848 8384 ns/op 11185 B/op 167 allocs/op BenchmarkStandardUnmarshalSmall-10 215502 5484 ns/op 2672 B/op 66 allocs/op BenchmarkUnmarshalLarge-10 5 227486309 ns/op 318321257 B/op 4925295 allocs/op BenchmarkStandardUnmarshalLarge-10 6 185993493 ns/op 99390094 B/op 1749996 allocs/op
Important notes ¶
- This doesn't support numbers larger than an Int64. If you need that, you need to use a string.
- This doesn't support anything other than decimal notation, but the JSON standard does. If someone needs it I'll add it.
- This does not have []byte conversion to string as the standard lib provides.
- There are likely bugs in here.
Examples ¶
Example of unmarshalling a JSON file:
f, err := os.Open("some/file/path.json") if err != nil { // Do something } dir, err := UnmarshalJSON(ctx, f) if err != nil { // Do something }
Example of creating a JSON object via the library:
dir := MustNewDir( "", MustNewFile("First Name", "John"), MustNewFile("Last Name", "Doak"), MustNewDir( "Identities", MustNewFile("EmployeeID", 10), MustNewFile("SSNumber", "999-99-9999"), ), )
Example of marshaling a Directory:
f, err := os.OpenFile("some/file/path.json", os.O_CREATE+os.O_RDWR, 0700) if err != nil { // Do something } defer f.Close() if err := MarshalJSON(f, dir); err != nil { // Do something }
Example of getting a JSON value by field name:
f, err := dir.GetFile("Identities/EmployeeID") if err != nil { // Do something }
Same example, but we are okay with zero values if the field doesn't exist:
f, _ := dir.GetFile("Identities/EmployeeID")
Get the type a File holds:
t := f.JSONType()
Get a value the easiest way when you aren't sure what it is:
v := f.Any() // Now you have to switch on types nil, string, bool, int64 or float // In order to use it.
Get a value from a Directory when you care about all the details (uck):
f, err := dir.GetFile("Identities/EmployeeID") if err != nil { fmt.Println("EmployeeID was not set") } else if f.Type() == FTNull { fmt.Println("EmployeeID was explicitly not set") }else { id, err := f.Int() if err != nil { // Do something } fmt.Println("EmployeeID is ", id) }
Get a value from a Directory when zero values will do if set to null or doesn't exist:
f, _ := dir.GetFile("Identities/EmployeeID") fmt.Println(f.StringOrZV()) // There is also BoolOrZV(), IntOrZV(), ...
Put the value in an fs.FS and walk the JSON:
// Note: this example can be found in examples/dirwalk fsys := NewFSFromDir(dir) fs.WalkDir(fsys, ".", func(p string, d fs.DirEntry, err error) error { if err != nil { log.Fatal(err) } p = path.Clean(p) switch x := d.(type) { case jsonfs.File: fmt.Printf("%s:%s:%v\n", p, x.JSONType(), x.Any()) case jsonfs.Directory: fmt.Printf("%s/\n", p) } return nil })
Index ¶
- Constants
- func Append[FD FileOrDir](array Directory, filesOrDirs ...FD) error
- func ArrayDirName(name string) string
- func ArraySet[FD FileOrDir](array Directory, index int, fd FD) error
- func ByteSlice2String(bs []byte) string
- func CP[FD FileOrDir](fileOrDir FD) FD
- func DirNameFromArray(name string) string
- func IsArray(fsys fs.ReadDirFS, path string) (bool, error)
- func MarshalJSON(w io.Writer, d Directory) error
- func SkipSpace(b *bufio.Reader)
- func UnmarshalStream(ctx context.Context, r io.Reader) chan Stream
- func UnsafeGetBytes(s string) []byte
- func ValueCheck(b *bufio.Reader) (next, error)
- func WriteOut[S Writeable](w io.Writer, values ...S) error
- type Directory
- func MustNewArray(name string, filesOrDirs ...any) Directory
- func MustNewDir(name string, filesOrDirs ...any) Directory
- func NewArray(name string, filesOrDirs ...any) (Directory, error)
- func NewDir(name string, filesOrDirs ...any) (Directory, error)
- func UnmarshalJSON(r io.Reader) (Directory, error)
- func (d Directory) Close() error
- func (d Directory) EncodeJSON(w io.Writer) error
- func (d Directory) GetDir(name string) (Directory, error)
- func (d Directory) GetFile(name string) (File, error)
- func (d Directory) GetObjects() chan Object
- func (d Directory) Info() (fs.FileInfo, error)
- func (d Directory) IsDir() bool
- func (d Directory) Len() int
- func (d Directory) Name() string
- func (d Directory) Read([]byte) (int, error)
- func (d Directory) ReadDir(n int) ([]fs.DirEntry, error)
- func (d Directory) Remove(name string) error
- func (d Directory) RemoveAll(name string) error
- func (d Directory) Set(filesOrDirs ...any) error
- func (d Directory) Stat() (fs.FileInfo, error)
- func (d Directory) Type() fs.FileMode
- func (d Directory) WriteFile(name string, data []byte) error
- type DirectoryFS
- type DiskFS
- func (f DiskFS) Directory(name string) (Directory, error)
- func (f DiskFS) Mkdir(p string, perm fs.FileMode) error
- func (f DiskFS) MkdirAll(p string, perm fs.FileMode) error
- func (f DiskFS) Open(name string) (fs.File, error)
- func (f DiskFS) OpenFile(name string, perms fs.FileMode, options ...gopherfs.OFOption) (fs.File, error)
- func (f DiskFS) ReadDir(name string) ([]fs.DirEntry, error)
- func (f DiskFS) ReadFile(name string) ([]byte, error)
- func (f DiskFS) Remove(name string) error
- func (f DiskFS) RemoveAll(path string) error
- func (f DiskFS) Stat(name string) (fs.FileInfo, error)
- func (f DiskFS) Sub(dir string) (fs.FS, error)
- func (f DiskFS) WriteFile(name string, data []byte, perm fs.FileMode) error
- type FS
- type File
- func (f File) Any() any
- func (f File) Bool() (bool, error)
- func (f File) BoolorZV() bool
- func (f File) Close() error
- func (f File) EncodeJSON(w io.Writer) error
- func (f File) Float() (float64, error)
- func (f File) FloatOrZV() float64
- func (f File) Info() (fs.FileInfo, error)
- func (f File) Int() (int64, error)
- func (f File) IntOrZV() int64
- func (f File) IsDir() bool
- func (f File) JSONType() FileType
- func (f File) Name() string
- func (f File) Read(dst []byte) (int, error)
- func (f File) Stat() (fs.FileInfo, error)
- func (f File) String() (string, error)
- func (f File) StringOrZV() string
- func (f File) Type() fs.FileMode
- type FileInfo
- type FileOrDir
- type FileType
- type MemFS
- func (f MemFS) MkdirAll(p string, perm fs.FileMode) error
- func (f MemFS) Open(name string) (fs.File, error)
- func (m MemFS) OpenFile(name string, perms fs.FileMode, options ...gopherfs.OFOption) (fs.File, error)
- func (f MemFS) ReadDir(name string) ([]fs.DirEntry, error)
- func (f MemFS) ReadFile(name string) ([]byte, error)
- func (f MemFS) Remove(name string) error
- func (f MemFS) RemoveAll(path string) error
- func (f MemFS) Stat(name string) (fs.FileInfo, error)
- func (f MemFS) Sub(dir string) (fs.FS, error)
- func (f MemFS) WriteFile(name string, data []byte, perm fs.FileMode) error
- type ObjType
- type Object
- type Stream
- type Writeable
Constants ¶
const ( OTUnknown = 0 OTFile = 1 OTDir = 2 )
Variables ¶
This section is empty.
Functions ¶
func Append ¶
Append appends to the Directory array all filesOrDirs passed. A nil FileOrDir value will append a JSON null.
func ArrayDirName ¶
func ArraySet ¶
ArraySet sets the value at index to fd. A nil value passed as fd will result in a null value being set.
func ByteSlice2String ¶
func CP ¶
func CP[FD FileOrDir](fileOrDir FD) FD
CP will make a copy of the File or Directory and return it. The modtime of the new directory and its files is the same as the old one.
func DirNameFromArray ¶
func IsArray ¶
IsArray reads a directory in fsys at path to determine if it is an array. Note: I wish there was a better way, but it would have to be portable across filesystems and has to deal with people doing adhoc writes to the filesystem.
func MarshalJSON ¶
MarshalJSON takes a Directory and outputs it as JSON to a file writer.
func UnmarshalStream ¶
UnmarshalStream unmarshals a stream of JSON objects from a reader. This will handle both streams of objects.
func UnsafeGetBytes ¶
UnsafeGetBytes extracts the []byte from a string. Use cautiously.
func ValueCheck ¶
ValueCheck tells what the next JSON value in a *bufio.Reader is.
Types ¶
type Directory ¶
type Directory struct {
// contains filtered or unexported fields
}
Directory represents an object or array in JSON nomenclature.
func MustNewArray ¶
MustNewArray is like NewArray, but errors cause a panic.
func MustNewDir ¶
MustNewDir is like NewDirectory, but errors cause a panic.
func NewArray ¶
NewArray creates a new Directory that represents a JSON array. filesOrDirs that have names will have them overridden.
func NewDir ¶
NewDir creates a new Directory with name and includes the files and directories passed. All passed Directories and Files must have a name. A top level directory does not have to have a name.
func UnmarshalJSON ¶
UnmarshalJSON unmarshals a single JSON object from an io.Reader. This object must be an object inside {}. This should only be used for reading a file or single object contained in an io.Reader. We will use a bufio.Reader underneath, so this reader is not usable after.
func (Directory) EncodeJSON ¶
EncodeJSON encodes the Directory as JSON into the io.Writer passed.
func (Directory) GetObjects ¶
func (Directory) Set ¶
Set will set sub directories or files in the Directory. If a file or Directory already exist, it will be overwritten. This does not work if the Directory is an array.
type DirectoryFS ¶
type DirectoryFS interface { // Directory retrieves a Directory from an FS. Directory(path string) (Directory, error) }
DirectoryFS provides methods for reading and writing a Directory to a Filesystem.
type DiskFS ¶
type DiskFS struct {
// contains filtered or unexported fields
}
DiskFS provides an FS for OS access. It will treat everything in the filesystem from the root down as JSON. So directories represent JSON objects or arrays while files represent JSON values. Note: I haven't tested this or done much with this.
func NewDiskFS ¶
NewDiskFS creates a new DiskFS rooted at path string. The path should represent a new directory and must not exist. MkdirAll() will be called with perms 0700. If you wish to open an existing path, use OpenDiskFS().
func OpenDiskFS ¶
OpenDiskFS opens a DiskFS that exists on the filesystem at path.
func (DiskFS) Directory ¶
Directory retrieves a Directory from path. Changes to the Directory will not make changes to disk. If you wish to sync this copy of the Directory to disk, use WriteDir().
func (DiskFS) Mkdir ¶
Mkdir creates a directory named "p" with permissions "perm". perm must be 0700, 0770, 0707 or 0777.
func (DiskFS) MkdirAll ¶
MkdirAll creates a directory named path, along with any necessary parents, and returns nil, or else returns an error. The permission bits perm (before umask) are used for all directories that MkdirAll creates. If path is already a directory, MkdirAll does nothing and returns nil. perm must be 0700, 0770, 0707, 0777. This implements github.com/gopherfs/fs.MkdirAllFS.MkdirAll.
func (DiskFS) OpenFile ¶
func (f DiskFS) OpenFile(name string, perms fs.FileMode, options ...gopherfs.OFOption) (fs.File, error)
OpenFile represents github.com/gopherfs/fs.OpenFiler. A file can only be opened in 0400, 0440, 0404, 0444. A Directory in 0700, 0770, 0707, 0777.
func (DiskFS) Remove ¶
Remove removes a file or directory (empty) at path "name". This implements github.com/gopherfs/fs.Remove.Remove .
func (DiskFS) RemoveAll ¶
RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error). If there is an error, it will be of type *fs.PathError.
type FS ¶
type FS interface { fs.FS fs.ReadDirFS fs.ReadFileFS fs.StatFS fs.SubFS gopherfs.MkdirAllFS gopherfs.Remove gopherfs.Writer }
FS details the interfaces that a filesytem must have in order to be used by jsonfs purposes. We do not honor filesytems interfaces outside this package at this time.
type File ¶
type File struct {
// contains filtered or unexported fields
}
File represents a value in JSON. This can be a string, bool or number. All files are readonly.
func MustNewFile ¶
MustNewFile is like NewFile except any error panics.
func NewFile ¶
NewFile creates a new file named "name" with value []byte. Files created with NewFile cannot have .Read() called, as this only works when opened from FS or a Directory. This simply is used to help construct a JSON value. value can be any type of int, string, bool or float. A nil value stands for a JSON null.
func (File) EncodeJSON ¶
EncodeJSON outputs the file data as into the writer.
func (File) StringOrZV ¶
type FileInfo ¶
type FileInfo struct {
// contains filtered or unexported fields
}
FileInfo implements fs.FileInfo.
type FileOrDir ¶
type FileOrDir interface {
// contains filtered or unexported methods
}
FileOrDir stands can hold a File or Directory.
type FileType ¶
type FileType uint8
FileType represents the data stored in a File.
type MemFS ¶
type MemFS struct {
// contains filtered or unexported fields
}
MemFS represents an inmemory filesystem for storing JSON data. It can be used with tools that work on fs.FS to do introspection on the JSON data or make data modifications. The normal way to use MemFS is for a single JSON entry.
func (MemFS) MkdirAll ¶
MkdirAll creates a directory named path, along with any necessary parents, and returns nil, or else returns an error. The permission bits perm (before umask) are used for all directories that MkdirAll creates, which must be 2147483940 (fs.ModeDir + 0444). If path is already a directory, MkdirAll does nothing and returns nil. This implements github.com/gopherfs/fs.MkdirAllFS.MkdirAll. TODO(jdoak): Move logic to Directory.MkdirAll and take a lock.
func (MemFS) OpenFile ¶
func (m MemFS) OpenFile(name string, perms fs.FileMode, options ...gopherfs.OFOption) (fs.File, error)
OpenFile implements gopherfs.OpenFiler. Perms are ignored except for the IsDir directive.
func (MemFS) Remove ¶
Remove removes a file or directory (empty) at path "name". This implements github.com/gopherfs/fs.Remove.Remove .
func (MemFS) RemoveAll ¶
RemoveAll removes path and any children it contains. It removes everything it can but returns the first error it encounters. If the path does not exist, RemoveAll returns nil (no error). If there is an error, it will be of type *fs.PathError.