ABAP String Templates: Formatting Strings with |{ }|

Category
ABAP-Statements
Published
Author
Johannes

String Templates (also called template strings) enable elegant creation of strings with embedded expressions and formatting options. They are delimited with |...| and contain expressions in {...}.

Syntax

|Literal text { expression } more text|
|{ expression OPTION = value }|

Basic Principle

  • Text between |...| is interpreted as a string
  • {...} contains expressions that are evaluated and inserted
  • Formatting options control the presentation
  • Escape sequences for special characters: \|, \{, \}, \\, \n, \r, \t

Examples

1. Simple Interpolation

DATA: lv_name TYPE string VALUE 'Max',
lv_age TYPE i VALUE 30.
" Classic with CONCATENATE
DATA: lv_text TYPE string.
CONCATENATE 'Hello ' lv_name ', you are ' lv_age ' years old.'
INTO lv_text.
" Modern with String Template
DATA(lv_text2) = |Hello { lv_name }, you are { lv_age } years old.|.
WRITE: / lv_text2. " Hello Max, you are 30 years old.

2. Expressions in Templates

DATA: lv_a TYPE i VALUE 10,
lv_b TYPE i VALUE 5.
" Embed calculations directly
DATA(lv_calc) = |{ lv_a } + { lv_b } = { lv_a + lv_b }|.
WRITE: / lv_calc. " 10 + 5 = 15
" Method calls
DATA(lv_upper) = |Name: { to_upper( lv_name ) }|.
WRITE: / lv_upper. " Name: MAX
" Conditional expressions
DATA(lv_status) = |Status: { COND #( WHEN lv_age >= 18 THEN 'Adult' ELSE 'Minor' ) }|.

3. Number Formatting (WIDTH, ALIGN, PAD)

DATA: lv_num TYPE i VALUE 42.
" Minimum width with WIDTH
DATA(lv_w) = |Number: { lv_num WIDTH = 10 }|.
WRITE: / lv_w. " Number: 42
" Alignment with ALIGN
DATA(lv_left) = |[{ lv_num WIDTH = 10 ALIGN = LEFT }]|.
DATA(lv_right) = |[{ lv_num WIDTH = 10 ALIGN = RIGHT }]|.
DATA(lv_center) = |[{ lv_num WIDTH = 10 ALIGN = CENTER }]|.
WRITE: / lv_left. " [42 ]
WRITE: / lv_right. " [ 42]
WRITE: / lv_center. " [ 42 ]
" Fill character with PAD
DATA(lv_padded) = |{ lv_num WIDTH = 6 ALIGN = RIGHT PAD = '0' }|.
WRITE: / lv_padded. " 000042

4. Formatting Decimal Numbers (DECIMALS, SIGN)

DATA: lv_amount TYPE p DECIMALS 2 VALUE '-1234.56'.
" Specify decimal places
DATA(lv_d1) = |Amount: { lv_amount DECIMALS = 2 }|.
WRITE: / lv_d1. " Amount: -1234.56
" Sign position
DATA(lv_sign_left) = |{ lv_amount SIGN = LEFT }|. " -1234.56
DATA(lv_sign_right) = |{ lv_amount SIGN = RIGHT }|. " 1234.56-
DATA(lv_sign_leftplus) = |{ lv_amount SIGN = LEFTPLUS }|. " -1234.56 (+ for positive)
" Thousands separator
DATA: lv_big TYPE p DECIMALS 2 VALUE '1234567.89'.
DATA(lv_sep) = |{ lv_big NUMBER = USER }|. " User format (e.g., 1,234,567.89)
DATA(lv_raw) = |{ lv_big NUMBER = RAW }|. " Without formatting

5. Date Formatting (DATE)

DATA: lv_date TYPE d VALUE '20241115'.
" Various date formats
DATA(lv_d_raw) = |{ lv_date DATE = RAW }|. " 20241115
DATA(lv_d_iso) = |{ lv_date DATE = ISO }|. " 2024-11-15
DATA(lv_d_user) = |{ lv_date DATE = USER }|. " User format
DATA(lv_d_env) = |{ lv_date DATE = ENVIRONMENT }|.
WRITE: / 'RAW:', lv_d_raw.
WRITE: / 'ISO:', lv_d_iso.
WRITE: / 'USER:', lv_d_user.

6. Time Formatting (TIME)

DATA: lv_time TYPE t VALUE '143025'.
" Various time formats
DATA(lv_t_raw) = |{ lv_time TIME = RAW }|. " 143025
DATA(lv_t_iso) = |{ lv_time TIME = ISO }|. " 14:30:25
DATA(lv_t_user) = |{ lv_time TIME = USER }|. " User format
WRITE: / 'Time:', lv_t_iso.

7. Timestamp Formatting (TIMESTAMP)

DATA: lv_ts TYPE timestamp.
GET TIME STAMP FIELD lv_ts.
DATA(lv_ts_iso) = |{ lv_ts TIMESTAMP = ISO }|.
DATA(lv_ts_space) = |{ lv_ts TIMESTAMP = SPACE }|.
DATA(lv_ts_user) = |{ lv_ts TIMESTAMP = USER }|.
WRITE: / 'Timestamp:', lv_ts_iso.

8. Alpha Conversion (ALPHA)

DATA: lv_matnr TYPE string VALUE '000000000000012345'.
" Alpha OUT: Remove leading zeros
DATA(lv_without_zeros) = |{ lv_matnr ALPHA = OUT }|.
WRITE: / lv_without_zeros. " 12345
" Alpha IN: Add leading zeros
DATA: lv_short TYPE string VALUE '12345'.
DATA(lv_with_zeros) = |{ lv_short ALPHA = IN WIDTH = 18 }|.
WRITE: / lv_with_zeros. " 000000000000012345

9. Case Conversion (CASE)

DATA: lv_text TYPE string VALUE 'Hello World'.
DATA(lv_upper) = |{ lv_text CASE = UPPER }|. " HELLO WORLD
DATA(lv_lower) = |{ lv_text CASE = LOWER }|. " hello world
DATA(lv_raw) = |{ lv_text CASE = RAW }|. " Hello World
WRITE: / lv_upper.
WRITE: / lv_lower.

10. Escape Sequences

" Escape special characters
DATA(lv_pipe) = |Pipe: \| and curly braces: \{ \}|.
DATA(lv_newline) = |Line 1\nLine 2|.
DATA(lv_tab) = |Column1\tColumn2|.
DATA(lv_backsl) = |Path: C:\\Folder\\File|.
WRITE: / lv_pipe.
WRITE: / lv_newline.
WRITE: / lv_tab.

11. Multi-line String Templates

" Strings can span multiple lines
DATA(lv_multi) = |This is a | &&
|multi-line | &&
|string.|.
" Or with \n for actual line breaks
DATA(lv_lines) = |Line 1\n| &&
|Line 2\n| &&
|Line 3|.
cl_demo_output=>display( lv_lines ).

12. Combination with COND and SWITCH

DATA: lv_status TYPE i VALUE 2.
DATA(lv_msg) = |Status: { SWITCH string( lv_status
WHEN 1 THEN 'New'
WHEN 2 THEN 'In Progress'
WHEN 3 THEN 'Completed'
ELSE 'Unknown'
) }|.
WRITE: / lv_msg. " Status: In Progress

13. Embedding Structure Fields

TYPES: BEGIN OF ty_customer,
id TYPE i,
name TYPE string,
city TYPE string,
END OF ty_customer.
DATA: ls_customer TYPE ty_customer.
ls_customer = VALUE #( id = 1001 name = 'Miller GmbH' city = 'Berlin' ).
DATA(lv_info) = |Customer { ls_customer-id }: { ls_customer-name } from { ls_customer-city }|.
WRITE: / lv_info. " Customer 1001: Miller GmbH from Berlin

14. Table Access in Templates

DATA: lt_names TYPE TABLE OF string.
lt_names = VALUE #( ( `Anna` ) ( `Bernd` ) ( `Clara` ) ).
" Direct table access
DATA(lv_first) = |First entry: { lt_names[ 1 ] }|.
WRITE: / lv_first. " First entry: Anna
" With OPTIONAL for safe access
DATA(lv_safe) = |Entry: { VALUE #( lt_names[ 999 ] OPTIONAL ) }|.

15. String Templates in Loops

LOOP AT lt_names INTO DATA(lv_name).
DATA(lv_line) = |{ sy-tabix }. { lv_name }|.
WRITE: / lv_line.
ENDLOOP.
" Output:
" 1. Anna
" 2. Bernd
" 3. Clara

16. String Templates for SQL

" Building dynamic SQL (Caution: SQL Injection!)
DATA: lv_field TYPE string VALUE 'CARRID',
lv_value TYPE string VALUE 'LH'.
" Better: Use parameters instead of string concatenation for SQL

17. Combining Formatting Options

DATA: lv_price TYPE p DECIMALS 2 VALUE '1234.50'.
DATA(lv_formatted) = |Price: { lv_price
DECIMALS = 2
SIGN = LEFT
WIDTH = 12
ALIGN = RIGHT
PAD = ' '
} EUR|.
WRITE: / lv_formatted. " Price: 1234.50 EUR

18. XSD Format for Boolean

DATA: lv_flag TYPE abap_bool VALUE abap_true.
" XSD format: true/false instead of X/''
DATA(lv_xsd) = |Active: { xsdbool( lv_flag ) }|.
WRITE: / lv_xsd. " Active: X
" Custom representation
DATA(lv_custom) = |Active: { COND #( WHEN lv_flag = abap_true THEN 'Yes' ELSE 'No' ) }|.

Formatting Options Overview

OptionValuesDescription
WIDTHNumberMinimum width
ALIGNLEFT, RIGHT, CENTERAlignment
PADCharacterFill character
CASEUPPER, LOWER, RAWCase conversion
SIGNLEFT, RIGHT, LEFTPLUS, RIGHTPLUSSign position
DECIMALSNumberDecimal places
NUMBERRAW, USER, ENVIRONMENTNumber format
DATERAW, ISO, USER, ENVIRONMENTDate format
TIMERAW, ISO, USER, ENVIRONMENTTime format
TIMESTAMPISO, SPACE, USERTimestamp format
ALPHAIN, OUT, RAWAlpha conversion
CURRENCYCurrency codeCurrency format
COUNTRYCountry codeCountry format

Comparison: Classic vs. String Templates

" === CLASSIC ===
DATA: lv_result TYPE string.
CONCATENATE 'Customer' ls_customer-id ':' ls_customer-name
INTO lv_result SEPARATED BY space.
" Or with &&
lv_result = 'Customer ' && ls_customer-id && ': ' && ls_customer-name.
" === MODERN WITH STRING TEMPLATES ===
DATA(lv_result2) = |Customer { ls_customer-id }: { ls_customer-name }|.

Best Practices

  • String templates begin and end with | (pipe character).
  • Expressions in {...} are evaluated at runtime.
  • Escape sequences: \| for pipe, \{ \} for braces, \n for line break.
  • Formatting options often make WRITE TO and conversion routines obsolete.
  • ALPHA = OUT is practical for material numbers without leading zeros.
  • DATE = ISO provides standardized format (YYYY-MM-DD).
  • Combine with COND and SWITCH for conditional text.
  • String templates are performant – prefer them over CONCATENATE.
  • Caution with dynamic SQL – use parameters instead of string interpolation.
  • NUMBER = USER respects user settings for decimal/thousands separators.