Last active
July 13, 2025 17:48
-
-
Save rponte/06f228cc370a4cbba6b23aae88301711 to your computer and use it in GitHub Desktop.
Example of a simple TLV parser (decoder)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import java.util.LinkedHashMap; | |
| import java.util.Map; | |
| import org.apache.logging.log4j.LogManager; | |
| import org.apache.logging.log4j.Logger; | |
| /** | |
| * Class responsible for parsing an encoded-text in TLV (Tag-Length-Value) format | |
| */ | |
| public class TLVParser { | |
| private static final Logger logger = LogManager.getLogger(TLVParser.class); | |
| /** | |
| * The minimum TLV size allowed | |
| */ | |
| private static final int MIN_TLV_SIZE = 7; | |
| /** | |
| * Holds the original TLV encoded-text | |
| */ | |
| private final String tlv; | |
| /** | |
| * Holds all tags keeping their insertion order | |
| */ | |
| private final Map<String, String> tlvData = new LinkedHashMap<>(); | |
| /** | |
| * Creates a parser for the specified <code>tlv</code> | |
| * | |
| * @param tlv the TLV encoded-text that will be parsed | |
| */ | |
| public TLVParser(String tlv) { | |
| this.tlv = tlv; | |
| parses(tlv); | |
| } | |
| /** | |
| * Parses TLV encoded-text into a Map | |
| */ | |
| private void parses(String tlv) { | |
| if (tlv == null) { | |
| logger.error("Invalid TLV data: can not be null"); | |
| return; | |
| } | |
| if (tlv.length() < MIN_TLV_SIZE) { | |
| logger.error("Invalid TLV data: must be at least 7 characters long (tlv='" + tlv + "')"); | |
| return; | |
| } | |
| try { | |
| int i = 0; | |
| while (true) { | |
| String tag = tlv.substring(i, i+3); | |
| int len = Integer.parseInt(tlv.substring(i+3, i+6)); | |
| String value = tlv.substring(i+6, i+6+len); | |
| tlvData.put(tag, value); | |
| i = i+6+len; | |
| if (i >= tlv.length()) | |
| break; | |
| } | |
| } catch (Exception e) { | |
| logger.error("Unexpected error while parsing TLV data: tlv='" + tlv + "'"); | |
| } | |
| } | |
| /** | |
| * Finds the tag value by the specified <code>tagName</code> | |
| * | |
| * @param tagName the tag name | |
| * @return the tag value or <code>null</code> if not found | |
| */ | |
| public String getTag(String tagName) { | |
| return tlvData.get(tagName); | |
| } | |
| /** | |
| * Finds the tag value by the specified <code>tagName</code>. In case the tag is | |
| * not found, returns the <code>defaultValue</code> | |
| * | |
| * @param tagName the tag name | |
| * @param defaultValue the default value that will used in case a tag is not | |
| * found | |
| * @return the tag value or <code>defaultValue</code> if not found | |
| */ | |
| public String getTag(String tagName, String defaultValue) { | |
| String tagValue = getTag(tagName); | |
| if (tagValue == null) { | |
| return defaultValue; | |
| } | |
| return tagValue; | |
| } | |
| /** | |
| * Returns the total of tags found | |
| */ | |
| public int getTotalOfTags() { | |
| return tlvData.size(); | |
| } | |
| @Override | |
| public String toString() { | |
| return "TLVParser [tlv=" + tlv | |
| + ", totalOfTags=" + getTotalOfTags() | |
| + ", tlvData=" + tlvData | |
| + "]"; | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import static org.apache.commons.lang3.StringUtils.repeat; | |
| import static org.junit.Assert.assertEquals; | |
| import org.apache.commons.lang3.StringUtils; | |
| import org.junit.Test; | |
| public class TLVParserTest { | |
| @Test | |
| public void shouldParserTLVEncodedText() { | |
| String tlv = join("txt006rponte", | |
| "IN10042020", | |
| "in2009000000666", | |
| "lo100820019393", | |
| "bi201100000014299", | |
| "dt201007/03/1984", | |
| "dt300820:20:01", | |
| "501002GP", | |
| "wsc006 aa " | |
| ); | |
| TLVParser parser = new TLVParser(tlv); | |
| assertEquals("total of tags", 9, parser.getTotalOfTags()); | |
| assertEquals("tag: txt", "rponte", parser.getTag("txt")); | |
| assertEquals("tag: IN1", "2020", parser.getTag("IN1")); | |
| assertEquals("tag: in2", "000000666", parser.getTag("in2")); | |
| assertEquals("tag: lo1", "20019393", parser.getTag("lo1")); | |
| assertEquals("tag: bi2", "00000014299", parser.getTag("bi2")); | |
| assertEquals("tag: dt2", "07/03/1984", parser.getTag("dt2")); | |
| assertEquals("tag: dt3", "20:20:01", parser.getTag("dt3")); | |
| assertEquals("tag: 501", "GP", parser.getTag("501")); | |
| assertEquals("tag: wsc", " aa ", parser.getTag("wsc")); | |
| } | |
| @Test | |
| public void shouldParserTLVEncodedText_asMuchAsPossible() { | |
| String tlvWithInvalidTag = join("txt006rponte", | |
| "IN10042020", | |
| "in2009000000666", | |
| "lo100820019393", | |
| "inv0xx90", // invalid | |
| "aaa001a", | |
| "bbb001b" | |
| ); | |
| TLVParser parser = new TLVParser(tlvWithInvalidTag); | |
| assertEquals("total of tags", 4, parser.getTotalOfTags()); | |
| assertEquals("tag: txt", "rponte", parser.getTag("txt")); | |
| assertEquals("tag: IN1", "2020", parser.getTag("IN1")); | |
| assertEquals("tag: in2", "000000666", parser.getTag("in2")); | |
| assertEquals("tag: lo1", "20019393", parser.getTag("lo1")); | |
| assertEquals("tag: inv", null, parser.getTag("inv")); | |
| assertEquals("tag: aaa", null, parser.getTag("aaa")); | |
| assertEquals("tag: bbb", null, parser.getTag("bbb")); | |
| } | |
| @Test | |
| public void shouldParserTLVEncodedText_andReturnDefaultValueIfTagNotFound() { | |
| String tlv = join("aaa001a", | |
| "bbb001b" | |
| ); | |
| TLVParser parser = new TLVParser(tlv); | |
| assertEquals("total of tags", 2, parser.getTotalOfTags()); | |
| assertEquals("tag: aaa", "a", parser.getTag("aaa", "x")); | |
| assertEquals("tag: bbb", "b", parser.getTag("bbb", "y")); | |
| assertEquals("tag: ccc", "z", parser.getTag("ccc", "z")); | |
| } | |
| @Test | |
| public void shouldNotParserTLVEncodedText_whenTextHasLessThan7Characters() { | |
| String tlv = "abcdef"; | |
| TLVParser parser = new TLVParser(tlv); | |
| assertEquals("total of tags", 0, parser.getTotalOfTags()); | |
| } | |
| @Test | |
| public void shouldNotParserTLVEncodedText_whenTextIsNull() { | |
| String tlv = null; | |
| TLVParser parser = new TLVParser(tlv); | |
| assertEquals("total of tags", 0, parser.getTotalOfTags()); | |
| } | |
| @Test | |
| public void shouldNotParserTLVEncodedText_whenTextIsEmpty() { | |
| String tlv = ""; | |
| TLVParser parser = new TLVParser(tlv); | |
| assertEquals("total of tags", 0, parser.getTotalOfTags()); | |
| } | |
| @Test | |
| public void shouldNotParserTLVEncodedText_whenTextIsBlank() { | |
| String tlv = repeat(" ", 12); | |
| TLVParser parser = new TLVParser(tlv); | |
| assertEquals("total of tags", 0, parser.getTotalOfTags()); | |
| } | |
| private String join(String...tlv) { | |
| return StringUtils.join(tlv, ""); | |
| } | |
| } |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example of a TLV Encoder