Go: Marshal Method for FTL Messages

The FTL Go language API implements the marshal and unmarshal interfaces to convert message datatype.

Methods

The Message type has two methods to conveniently import and export message fields:

  • Marshal imports a Go structure or map into an FTL message.
  • Unmarshal exports the fields of an FTL message to a Go structure or map.

FTL marshaling and unmarshaling support only those maps with key type string.

Marshaling ignores nil as the field value of a Go structure and as a slice element.

FTL Numeric Field Types

FTL messages store all Go language integral types as TIB_FIELD_TYPE_LONG fields, corresponding to the Go language type int64.

FTL messages store all Go language floating-point types as TIB_FIELD_TYPE_DOUBLE fields, corresponding to the Go language type float64.

These designations apply within the following tables.

  • IT refers to any of the following integral types: int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, uintptr, bool.
  • FT refers to any of the following floating-point types: float32, float64.

Conversion of Go Types to FTL Fields
Go Type Marshals to FTL Message Field Type
IT

*IT

TIB_FIELD_TYPE_LONG
FT

*FT

TIB_FIELD_TYPE_DOUBLE
[]IT

*[]IT

[]*IT

*[]*IT

TIB_FIELD_TYPE_LONG_ARRAY
[]FT

*[]FT

[]*FT

*[]*FT

TIB_FIELD_TYPE_DOUBLE_ARRAY
string

*string

TIB_FIELD_TYPE_STRING
[]string

*[]string

[]*string

*[]*string

TIB_FIELD_TYPE_STRING_ARRAY
byte

*byte

Note: []*byte and *[]*byte are not allowed.

TIB_FIELD_TYPE_OPAQUE
time.Time

*time.Time

TIB_FIELD_TYPE_DATETIME
[]time.Time

*[]time.Time

[]*time.Time

*[]*time.Time

TIB_FIELD_TYPE_DATETIME_ARRAY
structure

pointer to structure

TIB_FIELD_TYPE_MESSAGE
embedded structure

pointer to embedded structure

individual fields within the message
ftl.Message

*ftl.Message

TIB_FIELD_TYPE_MESSAGE
[]ftl.Message

*[]ftl.Message

[]*ftl.Message

*[]*ftl.Message

TIB_FIELD_TYPE_MESSAGE_ARRAY
ftl.Inbox

*ftl.Inbox

TIB_FIELD_TYPE_INBOX
map[string] TIB_FIELD_TYPE_MESSAGE
Note: Each element of the map marshals into a field within the message, with the element key becoming the field name. The key type must be string.

Conversion of FTL Fields to Go Types
FTL Message Field Type Unmarshals to Go Type
TIB_FIELD_TYPE_LONG

Supports strict conversion.

IT

*IT

TIB_FIELD_TYPE_DOUBLE

Supports strict conversion.

FT

*FT

TIB_FIELD_TYPE_LONG_ARRAY []IT

*[]IT

[]*IT

*[]*IT

The target struct must be a slice, not an array.

TIB_FIELD_TYPE_DOUBLE_ARRAY []FT

*[]FT

[]*FT

*[]*FT

The target struct must be a slice, not an array.

TIB_FIELD_TYPE_STRING string

*string

TIB_FIELD_TYPE_STRING_ARRAY []string

*[]string

[]*string

*[]*string

The target struct must be a slice, not an array.

TIB_FIELD_TYPE_OPAQUE byte

*byte

TIB_FIELD_TYPE_DATETIME time.Time

*time.Time

TIB_FIELD_TYPE_DATETIME_ARRAY []time.Time

*[]time.Time

[]*time.Time

*[]*time.Time

The target struct must be a slice, not an array.

TIB_FIELD_TYPE_MESSAGE structure

embedded structure

ftl.Message

*ftl.Message

map[string]

*map[string]

TIB_FIELD_TYPE_MESSAGE_ARRAY Array of structures

[]ftl.Message

*[]ftl.Message

[]*ftl.Message

*[]*ftl.Message

[]map[string]

*[]map[string]

The target struct must be a slice, not an array.

TIB_FIELD_TYPE_INBOX ftl.Inbox

*ftl.Inbox

Field Tags in struct Definitions

For convenience, you can use field tags to guide marshaling and unmarshaling (similar to JSON processing). For example:

type myStructType struct {
    myFieldA   int `json:"my_field_A" ftl:"myFieldA"`
    myFieldB   int `json:"my_field_B" ftl:"myFieldB,strict"`
    myFieldC   int `json:"my_field_C" ftl:"myFieldC,zeromissing,omitzero"`
}

FTL software recognizes tags with this general form:

ftl:"<fieldname>[,<options>]"
  • <fieldname> is the name of a field in an FTL message, corresponding to the tagged field of the Go struct definition.
  • <options> is a comma-separated list of options to apply marshaling and unmarshaling the field (see the following table).
  • Separate the individual tags with a space character. Each line of the example illustrates this with a space between the JSON tag and FTL tag.

Option Description
strict When unmarshaling, enforce strict conversion for numeric fields.

In strict conversion, the sign of a numeric FTL field value must be compatible with the corresponding Go language field type. If it is not compatible, the unmarshal call returns an error.

For example, consider the effect of unmarshaling an FTL message field with type TIB_FIELD_TYPE_LONG. The value 100 fits into any unsigned Go field, or into an int8. But the value -1000 does not fit, and strict conversion would reject it.

Non-strict conversion could yield unpredictable values for values that do not fit.

zeromissing When unmarshaling, supply zero values for missing fields.

If the target Go struct definition specifies a field but a corresponding field does not exist in the source FTL message, then conversion sets the Go field to the zero value of its field type.

When this option is absent, unmarshaling does not modify the target Go field unless the field is present in the FTL message.

omitzero When marshaling, omit fields that contain the zero value.

This option is analogous to omitempty in JSON marshaling. If the Go field contains the zero value for its type, then marshaling does not add a corresponding field to the FTL message.

Zero values include false, 0, 0.0, "", a nil pointer or interface value, and any empty array, slice, map, structure, or string.

When this option is absent, marshaling adds an FTL field corresponding to every Go field.

format=<format_name> Marshal a Go field value that is itself a nested structure to an FTL message with format < format_name>. (Use this option when your FTL application definition manages all formats.)

When this option is absent, marshal a nested structure to an FTL message of unnamed dynamic format (that is, null format name).

Examples

The FTL go API provides a simple mechanism for marshaling structures into an FTL message, and for unmarshaing FTL messages into a structure. The use of field tags is similar to that found in the encoding/json package. Note that structure fields must be exportable in order to be marshaled or unmarshaled. In simple terms, this means the field name must begin with an upper-case letter.

Marshal/unmarshal also supports the use of maps, in the form map[string]interface{}. In this case, the FTL field name corresponds to the map entry key, and the FTL field value corresponds to the map entry value.

The following example unmarshals message data into a Go structure:

type HelloMessage struct {
	Type  string `ftl:"type"`
	Message string `ftl:"message"`
}
func main() {
	// Create an instance of the HelloMessage structure. Then unmarshal
	// the message into the structure, based on the tags attached to 
	// each field in the structure.
	//
	// Note that a pointer to the structure (or map) must be passed to 
	// the Unmarshal() function.
	helloMsg := HelloMessage{}
	if err = msg.Unmarshal(&helloMsg); err != nil {
	log.Fatal(ftl.ErrStack(err))
	}
	// Print the message contents, both as a formatted FTL message
	// (via the message String() function) and the as the
	// unmarshaled structure content (using go's %#v formatting directive).
	//
	log.Printf("message: %s\n", msg.String())
	log.Printf("Unmarshalled message: %#v\n", helloMsg)
}

The following example marshals Go structure data into a message:

func main() {
	msg, err := realm.NewMessage("helloworld")
	if err != nil {
	log.Fatal(ftl.ErrStack(err))
	}
	// Defer the destruction of the message.
	defer msg.Destroy()
	// Construct the message content into a map[string]interface{}.
	content := make(map[string]interface{})
	content["type"] = "hello"
	content["message"] = "hello world earth"
	// Marshal the message content into the FTL message. Note that a 
	// pointer to the map must be passed to msg.Marshal().
	if err = msg.Marshal(&content); err != nil {
	log.Fatal(ftl.ErrStack(err))
	}
}

For more information about FTL message marshaling/unmarshaling, see the samples in .../samples/src/golang/src/tibco.com/ftl-sample.