1

How to parse a MT940 swift message using C#?
Below is the message that I have to parse:

:20:MT940-1411201901
:25:1234567837710016 
:28C:008/201
:60F:C171224SAR145597,13
:61:2107221722D17000,NCHK219120//14218-102431Abnamb
:61:2107221722D17000,NCHK219120//14218-102431Abnamb VSP
:62F:C291124SAR145597,13

How can I realize that?

zx485
  • 28,498
  • 28
  • 50
  • 59
Candy
  • 407
  • 1
  • 7
  • 19
  • this is not real Swift message since there is no headers available. I can suggest you parse it as `^:\d\d\w?:.*`.But you have to check that multi-line fields are parsed properly. Also please note that some swift messages can have field codes like :20: in the field value so you have to predict such cases – oleksa Dec 27 '19 at 16:39
  • This is not XML even though the data types are called tags. See : https://www.sepaforcorporates.com/swift-for-corporates/quick-guide-swift-mt101-format/ – jdweng Dec 27 '19 at 23:03

1 Answers1

-1

Try code like below :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Globalization;

namespace ConsoleApplication1
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.txt";
        static void Main(string[] args)
        {
            MT940 mt940 = new MT940(FILENAME);
        }
    }
    public class MT940
    {
        const string TAG_PATTERN = @"^:(?'tag'[^:]+):(?'value'.*)";

        public string senderReference { get; set; }  //code 20
        public string authorisation { get; set; } // tag 25
        public string messageIndexTotal { get; set; } //tag 28D
        public Currency openingBalance { get; set; }  //60F
        public string firstTransaction { get; set; } //61
        public string secondTransaction { get; set; } //61
        public Currency closingBalance { get; set; } //62F

        public MT940(string filename)
        {
            StreamReader reader = new StreamReader(filename);
            string line = "";
            int transactionCount = 0;

            while ((line = reader.ReadLine()) != null)
            {
                if (line.StartsWith(":"))
                {
                    Match match = Regex.Match(line, TAG_PATTERN);
                    string tag = match.Groups["tag"].Value;
                    string value = match.Groups["value"].Value;

                    switch (tag)
                    {
                        case "20":
                            senderReference = value;
                            break;

                        case "25":
                            authorisation = value;
                            break;

                        case "28C":
                            messageIndexTotal = value;
                            break;

                        case "60F":
                            openingBalance = new Currency(value);
                            break;
                        case "61":
                            if (++transactionCount == 1)
                            {
                                firstTransaction = value;
                            }
                            else
                            {
                                secondTransaction = value;
                            }
                            break;

                        case "62F":
                            closingBalance = new Currency(value);
                            break;
                        default:
                            break;
                    }
                }
            }
        }

    }
    public class Currency
    {
        const string BALANCE_PATTERN = @"^(?'credit_debit'.)(?'date'.{6})(?'country_code'.{3})(?'amount'.*)";
        static CultureInfo culture = CultureInfo.GetCultureInfoByIetfLanguageTag("da");

        public DateTime date { get; set; }
        public string currencyCode { get; set; }
        public decimal amount { get; set; }

        public Currency(string input)
        {
            Match match = Regex.Match(input, BALANCE_PATTERN);

            string credit_debit = match.Groups["credit_debit"].Value;

            string dateStr = match.Groups["date"].Value;
            date = DateTime.ParseExact(dateStr, "yyMMdd", CultureInfo.InvariantCulture);

            currencyCode = match.Groups["country_code"].Value;

            string amountStr = match.Groups["amount"].Value;
            amount = decimal.Parse(amountStr, culture);
            amount *= credit_debit == "D" ? -1 : 1;
        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20
  • 1
    To write parser properly you have to understand the business rules. The 940 message can contain a lot of 61 fields (more than two). Also the 61 field can [be followed by the 86 field](https://quickstream.westpac.com.au/bankrec-docs/statements/mt940/#format-overview). 86 is the multiline field so reading file line by line is a little bit tricky. The sample code parses particular sample that was provided but fails on real MT940. Parsing Swift MT format requires good knowledge and is much more than SA should provide. – oleksa Dec 28 '19 at 09:45
  • `line.Split(new char[] {':'}` will loose the field value if it contains the ':' char, like `:86:the reason: to return funds` – oleksa Dec 28 '19 at 09:48
  • @oleksa : The link you provided does not specify the fields can contain a colon. Can you provide a reference? – jdweng Dec 28 '19 at 10:14
  • well, 86 field do not have any restriction and can contain the ':'. The 61 is multi line field actually and can have `Supplementary Details` on new line. Swift message parsing means that field 61 (for example) must be parsed as Value date, Entry date, Amoun, Code, Number, Reference mandatory fields. There are too many hidden gems to parse the Swift MT using the SO. This question can be finally solved as a part of the student education using the SO but not for production code – oleksa Dec 28 '19 at 10:29
  • I don't like going to regex unless necessary. I prefer string methods which are more efficient. I will change code. – jdweng Dec 28 '19 at 10:42
  • MT 940 can be parsed with (or without) regular expressions. I prefer to use standard libraries rather then reinvent the wheel each time. However you are right, regular expressions can be too complex (especially for swift parsing). Two steps can be introduced to avoid complex Regex expressions: first parses pairs tag-value (value as raw string) and each field value is parsed on second step according to the field tag format. This approach allows to divide parsing code on small parts that are understandable and maintainable. – oleksa Dec 28 '19 at 11:00
  • In this case I think regex make code more understandable. – jdweng Dec 28 '19 at 11:26