translate

package
v0.35.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2026 License: Apache-2.0 Imports: 12 Imported by: 4

README

translate

A JSON-defined mapping engine that copies and transforms data from a source object into a target object. A Pipeline is an ordered list of Rules; pipeline.Execute(inSchema, inObject, outSchema, outObject) runs them in order. Both objects are accessed through schema Getter/Setter interfaces, so the easiest carriers are mapof.Any values, which implement those interfaces already. Part of rosetta.

Go Reference

Rule types

Each rule is a JSON object whose keys select the rule kind:

  • value — write a static value to the target; ignores the source. {"value":"FIXED", "target":"target.path"}
  • path — copy a value from a source path to a target path, unchanged. {"path":"source.path", "target":"target.path"}
  • expression — run a Go template against the source and write the result. {"expression":"{{ … }}", "target":"target.path"}
  • append — append a value to a slice/collection at the target path. {"append":"VALUE", "target":"target.path"}
  • if — evaluate a Go template; run then rules when it returns "true", otherwise else. {"if":"{{ … }}", "then":[…], "else":[…]}
  • forEach — loop a source map/array, running rules for each item under the target path (optionally filtered). {"forEach":"source.path", "target":"target.path", "filter":"{{ … }}", "rules":[…]}
  • first — run a list of rules, stopping after the first to set a non-zero value at the target. {"first":"target.path", "rules":[…]}

What matters here

  • The rule kind is chosen by which key is present, not by an explicit type field. Rule.UnmarshalMap checks for append, value, path, expression, if, forEach, first in turn. A rule map carrying two of these keys is ambiguous — the first matched dispatch wins. Author one rule kind per object.
  • Source and target must each implement the schema Getter/Setter interfaces. Plain structs work only if they implement those interfaces (see schema); a mapof.Any is the path of least resistance because it implements them out of the box.
  • expression, if, and forEach's filter are Go templates evaluated against the SOURCE object. They can read anything in the source but write only via their rule's target — a template that "returns true" for if must emit the literal string "true".
  • Writes go through schema.Set/Append, which validate and may coerce. A translated value can be clamped, truncated, or rewritten by the target schema's rules (see the schema README) — the target schema is the final authority on stored values, not the rule.
  • NewFromJSON parses untrusted JSON and is fuzzed (FuzzNewFromJSON). Keep that fuzzer green when changing rule unmarshalling.

Documentation

Overview

Package translate maps data from one object into another using rules that can be defined in JSON.

A Pipeline is an ordered list of Rules, executed in order against a source and a target. Rules cover the mapping vocabulary: copy a value from one path to another, write a static value, evaluate a Go template expression, append to a collection, take the first rule that produces a result, branch on a condition, or iterate a source collection and apply nested rules to each element.

Both objects are read and written through the schema package's Getter and Setter interfaces, so a pipeline never needs to know the concrete Go types involved. That makes mapof.Any the easiest carrier on either end, since it implements those interfaces already.

Because a pipeline is just data, the mapping between two systems can be stored, shipped, and edited without recompiling anything.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Pipeline

type Pipeline []Rule

Pipeline represents a slice of Rule objects

func New

func New(rules ...Rule) Pipeline

New returns a new Pipeline object, populated with the provided rules

Example
// Define rules for the translation Pipeline
rules := []Rule{
	Expression("{{.firstName}} {{.lastName}}", "fullName"),
	Path("email", "email"),
	Value("person", "type"),
	Condition(`{{eq "M" .gender}}`, []Rule{
		Expression("{{.firstName}} is Male", "comment"),
	}, []Rule{
		Expression("{{.firstName}} is not Malr", "comment"),
	}),
}

// Add all of the rules to a new Pipeline
translator := New(rules...)
fmt.Println(translator)

func NewFromJSON

func NewFromJSON(jsonString string) (Pipeline, error)

NewFromJSON reads a JSON string and returns a Pipeline object

Example
// Import JSON from external source
rulesJSON := `[` + // nolint:scopeguard
	`{"expression": "{{.firstName}} {{.lastName}}", "target": "fullName"},
		{"path": "email", "target": "email"},
		{"value": "person", "target": "type"},
		{"if": "{{eq \"M\" .gender}}", "then": [
			{"target": "comment", "expression": "{{.firstName}} is Male"}
		], "else": [
			{"target": "comment", "expression": "{{.firstName}} is not Male"}
		]}
	]`

// Unmarshal JSON directly into a Pipeline
rules, _ := NewFromMap()
if json.Unmarshal([]byte(rulesJSON), &rules) != nil {
	fmt.Println("Error parsing JSON")
}

// Success!
fmt.Println(rules)

func NewFromMap

func NewFromMap(rules ...map[string]any) (Pipeline, error)

NewFromMap parses a slice of mapof.Any objects into a Pipeline

Example
// Define rules as a mapof.Any
rules := []map[string]any{
	{"expression": "{{.firstName}} {{.lastName}}", "target": "fullName"},
	{"path": "email", "target": "email"},
	{"value": "person", "target": "type"},
	{"if": `{{eq "M" .gender}}`, "then": []mapof.Any{
		{"target": "comment", "expression": "{{.firstName}} is Male"},
	}, "else": []mapof.Any{
		{"target": "comment", "expression": "{{.firstName}} is not Male"},
	}},
}

// Create a new Pipeline from the rules
if translator, err := NewFromMap(rules...); err != nil {
	fmt.Println(err)
} else {
	fmt.Println(translator)
}

func NewSliceOfPipelines added in v0.23.2

func NewSliceOfPipelines(slices [][]map[string]any) ([]Pipeline, error)

NewSliceOfPipelines parses a slice of maps into a slice of Pipelines. If any of the maps DOES NOT represent a valid Pipeline, then the function will return an error.

func (Pipeline) Execute

func (pipeline Pipeline) Execute(inSchema schema.Schema, inObject any, outSchema schema.Schema, outObject any) error

Execute runs each of the rules in the Pipeline

Example
// SOURCE DATA
sourceValue := mapof.Any{
	"firstName": "John",
	"lastName":  "Connor",
	"email":     "john@connor.mil",
	"gender":    "M",
}

// TARGET CONFIGURATION
targetSchema := schema.New(schema.Object{
	Properties: schema.ElementMap{
		"fullName": schema.String{},
		"email":    schema.String{Format: "email"},
		"type":     schema.String{},
		"comment":  schema.String{},
	},
})
targetValue := mapof.Any{}

// CREATE MAPPING RULES
rules, err := NewFromMap(
	mapof.Any{"target": "fullName", "expression": "{{.firstName}} {{.lastName}}"},
	mapof.Any{"target": "email", "path": "email"},
	mapof.Any{"target": "type", "value": "person"},
	mapof.Any{"if": "{{eq \"M\" .gender}}", "then": []mapof.Any{
		{"target": "comment", "expression": "{{.firstName}} is Male"},
	}, "else": []mapof.Any{
		{"target": "comment", "expression": "{{.firstName}} is not Male"},
	}},
)
derp.Report(err)

// MAP DATA FROM SOURCE TO TARGET
err = rules.Execute(schema.Wildcard(), sourceValue, targetSchema, &targetValue)
derp.Report(err)

// OUTPUT RESULTS
fmt.Println(targetValue.GetString("fullName"))
fmt.Println(targetValue.GetString("email"))
fmt.Println(targetValue.GetString("type"))
fmt.Println(targetValue.GetString("comment"))
Output:
John Connor
john@connor.mil
person
John is Male

func (Pipeline) IsEmpty

func (pipeline Pipeline) IsEmpty() bool

IsEmpty returns TRUE if the pipeline contains no rules.

func (Pipeline) MarshalJSON added in v0.23.2

func (pipeline Pipeline) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface, encoding the pipeline as an array of rule maps.

func (Pipeline) MarshalSliceOfMap added in v0.23.2

func (pipeline Pipeline) MarshalSliceOfMap() []map[string]any

MarshalSliceOfMap returns each rule in the pipeline as a map[string]any.

func (Pipeline) NotEmpty

func (pipeline Pipeline) NotEmpty() bool

NotEmpty returns TRUE if the pipeline contains at least one rule.

type Rule

type Rule struct {
	Runner
}

Rule represents a single mapping rule

func Append added in v0.24.0

func Append(append any, target string) Rule

Append creates a new Rule that writes a constant value to the output object

func Condition

func Condition(condition string, thenRules []Rule, elseRules []Rule) Rule

Condition creates a new Rule that executes a condition, and then runs a set of rules based on the result

func Expression

func Expression(expression string, target string) Rule

Expression creates a new Rule that executes a template expression

func First added in v0.25.19

func First(targetPath string, rules []map[string]any) Rule

First creates a new Rule that executes its sub-steps, stopping with the first one that returns a non-zero value

func ForEach

func ForEach(sourcePath string, targetPath string, filter string, rulesMap []map[string]any) Rule

ForEach creates a new Rule that copies a value from one location to another

func Path

func Path(from string, target string) Rule

Path creates a new Rule that copies a value from one location to another

func Value

func Value(value any, target string) Rule

Value creates a new Rule that writes a constant value to the output object

func (*Rule) MarshalJSON added in v0.23.2

func (rule *Rule) MarshalJSON() ([]byte, error)

MarshalJSON implements the json.Marshaler interface, encoding the rule's underlying Runner.

func (*Rule) MarshalMap added in v0.23.2

func (rule *Rule) MarshalMap() map[string]any

MarshalMap returns the rule's underlying Runner as a map[string]any.

func (*Rule) UnmarshalJSON

func (rule *Rule) UnmarshalJSON(data []byte) error

UnmarshalJSON implements the json.Unmarshaller interface

func (*Rule) UnmarshalMap

func (rule *Rule) UnmarshalMap(data mapof.Any) error

UnmarshalMap populates this object from a mapof.Any

type Runner

type Runner interface {

	// Execute runs the rule on the input object, and writes the result to the output object
	Execute(inSchema schema.Schema, inObject any, outSchema schema.Schema, outObject any) error

	// MarshalMap converts the object to a mapof.Any
	MarshalMap() map[string]any
}

Runner in the interface for objects that implement Rules

Jump to

Keyboard shortcuts

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