decoder

package
v1.16.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 17, 2025 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package decoder provides components for decoding HTTP request bodies of various content types.

Decoders implement the Decoder interface and are responsible for parsing request bodies based on the Content-Type header. Each decoder handles a specific content type and populates Go structures accordingly.

Built-in Decoders

  • JSON: Handles application/json using jsoniter for performance
  • XML: Handles application/xml using standard library encoding/xml
  • FormURL: Handles application/x-www-form-urlencoded for HTML forms
  • MultipartFormData: Handles multipart/form-data for file uploads and complex forms

Basic Usage

// Create decoders
jsonDecoder := decoder.NewJSON()
formDecoder := decoder.NewFormURL()

// Use with roamer
r := roamer.NewRoamer(
    roamer.WithDecoders(jsonDecoder, formDecoder),
)

Custom Decoders

Implement the Decoder interface to support custom content types:

type MyDecoder struct{}

func (d *MyDecoder) Decode(r *http.Request, ptr any) error {
    // Custom decoding logic
    return nil
}

func (d *MyDecoder) ContentType() string {
    return "application/my-format"
}

func (d *MyDecoder) Tag() string {
    return "myformat"
}

Thread Safety

All built-in decoders are safe for concurrent use and should be reused across multiple requests for optimal performance.

Index

Examples

Constants

View Source
const (
	// ContentTypeFormURL is the Content-Type header value for URL-encoded form data.
	// This is used to match requests with the appropriate decoder.
	ContentTypeFormURL = "application/x-www-form-urlencoded"

	// TagForm is the struct tag name used for URL-encoded form values.
	TagForm = "form"

	// SplitSymbol is the default character used to split form values when
	// multiple values are provided for the same field.
	SplitSymbol = ","
)
View Source
const (
	// ContentTypeJSON is the Content-Type header value for JSON requests.
	// This is used to match requests with the appropriate decoder.
	ContentTypeJSON = "application/json"

	// TagJSON is the struct tag name used for JSON field mapping.
	TagJSON = "json"
)
View Source
const (
	// ContentTypeMultipartFormData is the Content-Type header value for multipart form data.
	// This is used to match requests with the appropriate decoder.
	ContentTypeMultipartFormData = "multipart/form-data"

	// TagMultipart is the struct tag name used for multipart form values.
	TagMultipart = "multipart"
)
View Source
const (
	// ContentTypeXML is the Content-Type header value for XML requests.
	// This is used to match requests with the appropriate decoder.
	ContentTypeXML = "application/xml"

	// TagXML is the struct tag name used for XML field mapping.
	TagXML = "xml"
)

Variables

This section is empty.

Functions

func WithContentType

func WithContentType[T contentTypeSetter](contentType string) func(T)

WithContentType sets the Content-Type header value that a decoder will handle. Works with any decoder implementing contentTypeSetter interface.

Example:

// Handle custom content types
jsonDecoder := decoder.NewJSON(
    decoder.WithContentType("application/x-json"),
)

xmlDecoder := decoder.NewXML(
    decoder.WithContentType("text/xml"),
)

func WithSkipFilled added in v1.7.1

func WithSkipFilled[T skipFilledSetter](skip bool) func(T)

WithSkipFilled controls whether decoders skip fields with non-zero values. When true, existing values won't be overwritten by decoded values.

Example:

// Overwrite all fields, even if already filled
formDecoder := decoder.NewFormURL(
    decoder.WithSkipFilled(false),
)

Types

type FormURL

type FormURL struct {
	// contains filtered or unexported fields
}

FormURL is a decoder for handling URL-encoded form data (application/x-www-form-urlencoded).

This decoder processes HTML form submissions and parses them into Go structures. It supports both single and multiple values for the same form field, with automatic splitting of comma-separated values.

Value Splitting

By default, comma-separated form values are split into slices:

  • "tags=foo,bar,baz" -> []string{"foo", "bar", "baz"}

This behavior can be disabled using WithDisabledSplit().

Supported Target Types

  • Structs with "form" tags
  • map[string]string
  • map[string]any
  • map[string][]string

Thread Safety

The FormURL decoder is safe for concurrent use across multiple goroutines.

func NewFormURL

func NewFormURL(opts ...FormURLOptionsFunc) *FormURL

NewFormURL creates a FormURL decoder that handles application/x-www-form-urlencoded content. By default, it skips already filled fields and splits comma-separated values.

Example:

// Default form decoder
formDecoder := decoder.NewFormURL()

// Custom configuration
formDecoder := decoder.NewFormURL(
    decoder.WithDisabledSplit(),       // Don't split comma-separated values
    decoder.WithSplitSymbol(";"),      // Use semicolon as separator
)

// Example struct
type SearchRequest struct {
    Query string   `form:"q"`
    Tags  []string `form:"tags"` // Can handle "tags=foo,bar,baz"
}

func (*FormURL) ContentType

func (f *FormURL) ContentType() string

ContentType returns the Content-Type header value that this decoder handles. For the FormURL decoder, this is "application/x-www-form-urlencoded" by default. This method is used by the roamer package to match requests with the appropriate decoder.

func (*FormURL) Decode

func (f *FormURL) Decode(r *http.Request, ptr any) error

Decode parses URL-encoded form data into a struct or map. For structs, uses the "form" tag to map fields. For maps, populates with form field names as keys.

Parameters:

  • r: The HTTP request with form data.
  • ptr: Target pointer (struct or map).

Returns:

  • error: Error if decoding fails, nil if successful.

func (*FormURL) Tag added in v1.12.1

func (f *FormURL) Tag() string

Tag returns the struct tag name used for form field mapping. For the FormURL decoder, this is "form" by default.

type FormURLOptionsFunc

type FormURLOptionsFunc func(*FormURL)

FormURLOptionsFunc is a function type for configuring a FormURL decoder. It follows the functional options pattern to provide a clean and extensible API.

func WithDisabledSplit

func WithDisabledSplit() FormURLOptionsFunc

WithDisabledSplit disables automatic splitting of form values into slices.

Example: With splitting disabled, "tags=foo,bar,baz" will parse as a single string "foo,bar,baz" rather than a slice ["foo", "bar", "baz"]

func WithSplitSymbol

func WithSplitSymbol(splitSymbol string) FormURLOptionsFunc

WithSplitSymbol sets the character used for splitting form values (default: comma).

Example: With splitSymbol set to ";", a form field "tags=foo;bar;baz" will parse as a slice ["foo", "bar", "baz"]

type JSON

type JSON struct {
	// contains filtered or unexported fields
}

JSON is a decoder for handling JSON request bodies.

It uses the jsoniter library (github.com/json-iterator/go) for better performance compared to the standard library's encoding/json. The decoder is configured to be compatible with the standard library's behavior by default.

Performance

jsoniter provides significant performance improvements over encoding/json:

  • Faster encoding and decoding
  • Lower memory allocations
  • API-compatible with standard library

Thread Safety

The JSON decoder is safe for concurrent use across multiple goroutines.

func NewJSON

func NewJSON(opts ...JSONOptionsFunc) *JSON

NewJSON creates a JSON decoder for handling application/json content type.

By default, the decoder uses jsoniter with standard library compatible configuration, which provides better performance while maintaining the same behavior as encoding/json.

Configuration

The decoder can be customized using functional options:

  • WithContentType: Override the default "application/json" content type

Default Behavior

  • Content-Type: application/json
  • Tag name: json
  • Configuration: Compatible with standard library
  • Empty body handling: Treated as empty object/value

Parameters:

  • opts: Optional configuration functions to customize the decoder.

Returns:

  • *JSON: A configured JSON decoder instance.

Example:

// Basic JSON decoder
jsonDecoder := decoder.NewJSON()

// JSON decoder with custom content type
jsonDecoder := decoder.NewJSON(
    decoder.WithContentType("application/x-json"),
)

// Use with roamer
r := roamer.NewRoamer(roamer.WithDecoders(jsonDecoder))

// Example struct
type UserRequest struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age,omitempty"`
}
Example

ExampleNewJSON demonstrates how to create and use a JSON decoder.

package main

import (
	"bytes"
	"fmt"
	"net/http"

	"github.com/slipros/roamer"
	"github.com/slipros/roamer/decoder"
)

// readCloser wraps a bytes.Buffer to implement io.ReadCloser
type readCloser struct {
	*bytes.Buffer
}

func (rc *readCloser) Close() error {
	return nil
}

func main() {
	// Define a structure for JSON data
	type User struct {
		ID    int    `json:"id"`
		Name  string `json:"name"`
		Email string `json:"email"`
	}

	// Create a JSON decoder
	jsonDecoder := decoder.NewJSON()

	// Create a roamer instance with the JSON decoder
	r := roamer.NewRoamer(
		roamer.WithDecoders(jsonDecoder),
	)

	// Create a request with JSON body
	jsonBody := `{"id": 123, "name": "John Doe", "email": "[email protected]"}`
	req := &http.Request{
		Method: "POST",
		Header: http.Header{
			"Content-Type": {"application/json"},
		},
		Body:          &readCloser{bytes.NewBufferString(jsonBody)},
		ContentLength: int64(len(jsonBody)),
	}

	// Parse the request
	var user User
	err := r.Parse(req, &user)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("User ID: %d\n", user.ID)
	fmt.Printf("User Name: %s\n", user.Name)
	fmt.Printf("User Email: %s\n", user.Email)

}
Output:

User ID: 123
User Name: John Doe
User Email: [email protected]

func (*JSON) ContentType

func (j *JSON) ContentType() string

ContentType returns the Content-Type header value that this decoder handles. For the JSON decoder, this is "application/json" by default. This method is used by the roamer package to match requests with the appropriate decoder.

Example

ExampleJSON_ContentType demonstrates retrieving the content type handled by a JSON decoder.

package main

import (
	"fmt"

	"github.com/slipros/roamer/decoder"
)

func main() {
	d := decoder.NewJSON()
	fmt.Printf("Content-Type: %s\n", d.ContentType())
	fmt.Printf("Struct Tag: %s\n", d.Tag())

}
Output:

Content-Type: application/json
Struct Tag: json

func (*JSON) Decode

func (j *JSON) Decode(r *http.Request, ptr any) error

Decode parses a JSON request body into the provided pointer.

The method reads the request body from r.Body and decodes it into the destination pointed to by ptr using jsoniter for high-performance parsing. The decoder is compatible with standard library's encoding/json behavior.

Empty Body Handling

If the request body is empty (io.EOF), the method returns nil without error, leaving the destination in its zero state. This allows graceful handling of requests with no body content.

Error Handling

The method returns an error if:

  • JSON syntax is invalid
  • JSON structure doesn't match the destination type
  • Required fields are missing (when using json:",required" tag)
  • Type conversion fails

Errors from jsoniter are returned directly and can be examined for details.

Supported Types

  • Structs with json tags
  • Maps (map[string]any, map[string]string, etc.)
  • Slices and arrays
  • Basic types (when ptr is *string, *int, etc.)

Parameters:

  • r: The HTTP request containing the JSON body. Body will be read but not closed.
  • ptr: A pointer to the destination where decoded data will be stored. Must not be nil.

Returns:

  • error: An error if decoding fails, or nil if successful.

Example:

type UserRequest struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

var user UserRequest
err := jsonDecoder.Decode(r, &user)
if err != nil {
    // Handle JSON parsing error
    return fmt.Errorf("invalid JSON: %w", err)
}
Example

ExampleJSON_Decode demonstrates direct use of the JSON decoder's Decode method.

package main

import (
	"bytes"
	"fmt"
	"net/http"

	"github.com/slipros/roamer/decoder"
)

// readCloser wraps a bytes.Buffer to implement io.ReadCloser
type readCloser struct {
	*bytes.Buffer
}

func (rc *readCloser) Close() error {
	return nil
}

func main() {
	// Define a structure
	type Product struct {
		Name  string  `json:"name"`
		Price float64 `json:"price"`
	}

	// Create decoder
	jsonDecoder := decoder.NewJSON()

	// Create request with JSON body
	jsonBody := `{"name": "Laptop", "price": 999.99}`
	req := &http.Request{
		Method: "POST",
		Header: http.Header{
			"Content-Type": {"application/json"},
		},
		Body:          &readCloser{bytes.NewBufferString(jsonBody)},
		ContentLength: int64(len(jsonBody)),
	}

	// Decode directly
	var product Product
	err := jsonDecoder.Decode(req, &product)
	if err != nil {
		fmt.Printf("Error: %v\n", err)
		return
	}

	fmt.Printf("Product: %s, Price: $%.2f\n", product.Name, product.Price)

}
Output:

Product: Laptop, Price: $999.99

func (*JSON) Tag added in v1.12.1

func (j *JSON) Tag() string

Tag returns the struct tag name used for JSON field mapping. For the JSON decoder, this is "json" by default.

type JSONOptionsFunc

type JSONOptionsFunc = func(*JSON)

JSONOptionsFunc is a function type for configuring a JSON decoder. It follows the functional options pattern to provide a clean and extensible API.

type MultipartFile

type MultipartFile struct {
	// Key is the form field name associated with this file.
	Key string

	// File is the actual file content, which can be read as an io.Reader.
	// Remember to close this file when you're done with it to avoid resource leaks.
	File multipart.File

	// Header contains metadata about the uploaded file,
	// such as the original filename, content type, and size.
	Header *multipart.FileHeader
}

MultipartFile represents an uploaded file from a multipart/form-data request. Contains both file content and metadata.

Example:

type UploadRequest struct {
    Avatar *MultipartFile `multipart:"avatar"`
}

func (*MultipartFile) ContentType

func (f *MultipartFile) ContentType() string

ContentType returns the content type of the uploaded file.

Example:

// Check file type
contentType := req.Avatar.ContentType()
if contentType != "image/jpeg" && contentType != "image/png" {
    http.Error(w, "Only JPEG and PNG allowed", http.StatusBadRequest)
    return
}

func (*MultipartFile) Copy

func (f *MultipartFile) Copy() (MultipartFile, error)

Copy creates a duplicate of this MultipartFile with a new file handler. Useful when multiple processors need to read from the beginning of the file. Note: Both original and copied files must be closed to avoid resource leaks.

func (*MultipartFile) IsValid added in v1.7.1

func (f *MultipartFile) IsValid() bool

IsValid checks if the MultipartFile contains valid data. Returns false if any essential field is missing.

type MultipartFiles

type MultipartFiles []MultipartFile

MultipartFiles is a collection of MultipartFile objects. Use this to receive all files from a multipart request:

type UploadRequest struct {
    Files MultipartFiles `multipart:",allfiles"`
}

func (MultipartFiles) Close

func (mf MultipartFiles) Close() error

Close closes all file handles in the collection to prevent resource leaks.

Example:

func handleUpload(w http.ResponseWriter, r *http.Request) {
    var req UploadRequest
    if err := roamer.Parse(r, &req); err != nil {
        return
    }
    defer req.Files.Close() // Important: close all files
}

type MultipartFormData

type MultipartFormData struct {
	// contains filtered or unexported fields
}

MultipartFormData is a decoder for handling multipart form data (multipart/form-data), including file uploads and mixed form fields.

This decoder is specifically designed for handling HTML forms with file uploads, allowing simultaneous processing of regular form fields and uploaded files. It uses the "multipart" struct tag to map form fields to struct members.

File Upload Support

  • Single file uploads using *MultipartFile
  • Multiple file uploads using MultipartFiles
  • Access to file metadata (name, size, content-type)
  • Automatic cleanup when files are properly closed

Special Tags

  • multipart:",allfiles" - Captures all uploaded files into a MultipartFiles slice
  • multipart:"fieldname" - Maps to a specific form field or file input

Memory Management

Files larger than maxMemory (default 32MB) are stored in temporary disk files. Smaller files are kept in memory. The threshold can be configured using WithMaxMemory().

Thread Safety

The MultipartFormData decoder requires a structure cache set via SetStructureCache before use. After initialization, it is safe for concurrent use.

Important

Always close uploaded files after processing to prevent resource leaks:

defer req.Files.Close()

func NewMultipartFormData

func NewMultipartFormData(opts ...MultipartFormDataOptionsFunc) *MultipartFormData

NewMultipartFormData creates a decoder for multipart/form-data content, including file uploads. Handles both form fields and uploaded files.

Example:

// Default settings (32MB memory limit)
multipartDecoder := decoder.NewMultipartFormData()

// Custom memory limit
multipartDecoder := decoder.NewMultipartFormData(
    decoder.WithMaxMemory(10 << 20), // 10 MB
)

// Example struct
type UploadRequest struct {
    Title   string              `multipart:"title"`
    Avatar  *decoder.MultipartFile `multipart:"avatar"` // Single file
    Gallery decoder.MultipartFiles `multipart:",allfiles"` // All files
}

func (*MultipartFormData) ContentType

func (m *MultipartFormData) ContentType() string

ContentType returns the Content-Type header value that this decoder handles. For the MultipartFormData decoder, this is "multipart/form-data" by default. This method is used by the roamer package to match requests with the appropriate decoder.

func (*MultipartFormData) Decode

func (m *MultipartFormData) Decode(r *http.Request, ptr any) error

Decode parses multipart form data into a struct pointer. Handles both form fields and file uploads.

Parameters:

  • r: The HTTP request with multipart form data.
  • ptr: Target struct pointer.

Returns:

  • error: Error if decoding fails, nil if successful.

func (*MultipartFormData) SetStructureCache added in v1.12.0

func (m *MultipartFormData) SetStructureCache(cache *cache.Structure)

SetStructureCache assigns a structure cache to the decoder for improved performance. The cache stores precomputed field information to avoid reflection overhead on each request.

func (*MultipartFormData) Tag added in v1.12.1

func (m *MultipartFormData) Tag() string

Tag returns the struct tag name used for multipart field mapping. For the MultipartFormData decoder, this is "multipart" by default.

type MultipartFormDataOptionsFunc

type MultipartFormDataOptionsFunc = func(*MultipartFormData)

MultipartFormDataOptionsFunc is a function type for configuring a MultipartFormData decoder. It follows the functional options pattern to provide a clean and extensible API.

func WithMaxMemory

func WithMaxMemory(maxMemory int64) MultipartFormDataOptionsFunc

WithMaxMemory sets the maximum memory limit for parsing multipart data. Content beyond this limit will be stored in temporary files. Default is 32 MB.

Example: decoder.WithMaxMemory(10 << 20) // 10 MB

type XML

type XML struct {
	// contains filtered or unexported fields
}

XML is a decoder for handling XML request bodies. It uses the standard library's encoding/xml package for XML parsing.

func NewXML

func NewXML(opts ...XMLOptionsFunc) *XML

NewXML creates an XML decoder for handling application/xml content. Uses standard library's encoding/xml package for XML parsing.

Example:

// Default XML decoder
xmlDecoder := decoder.NewXML()

// With custom Content-Type
xmlDecoder := decoder.NewXML(decoder.WithContentType("text/xml"))

// Example struct
type BookRequest struct {
    Title  string `xml:"title"`
    Author string `xml:"author"`
}

func (*XML) ContentType

func (x *XML) ContentType() string

ContentType returns the Content-Type header value that this decoder handles. For the XML decoder, this is "application/xml" by default. This method is used by the roamer package to match requests with the appropriate decoder.

func (*XML) Decode

func (x *XML) Decode(r *http.Request, ptr any) error

Decode parses an XML request body into the provided pointer. Handles empty bodies gracefully as empty XML documents.

Parameters:

  • r: The HTTP request with XML body.
  • ptr: Target structure pointer.

Returns:

  • error: Error if decoding fails, nil if successful.

func (*XML) Tag added in v1.12.1

func (x *XML) Tag() string

Tag returns the struct tag name used for XML field mapping. For the XML decoder, this is "xml" by default.

type XMLOptionsFunc

type XMLOptionsFunc = func(*XML)

XMLOptionsFunc is a function type for configuring an XML decoder. It follows the functional options pattern to provide a clean and extensible API.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL