myCSharp.de - DIE C# und .NET Community
Willkommen auf myCSharp.de! Anmelden | kostenlos registrieren
 
 | Suche | FAQ

» Hauptmenü
myCSharp.de
» Startseite
» Forum
» FAQ
» Artikel
» C#-Snippets
» Jobbörse
» Suche
» Regeln
» Wie poste ich richtig?
» Forum-FAQ

Mitglieder
» Liste / Suche
» Wer ist wo online?

Ressourcen
» openbook: Visual C#
» openbook: OO
» Microsoft Docs

Team
» Kontakt
» Übersicht
» Wir über uns

» myCSharp.de Diskussionsforum
Du befindest Dich hier: Community-Index » Diskussionsforum » Gemeinschaft » .NET-Komponenten und C#-Snippets » Regex Mapper: Groups eines Matches in Properties eines Objekts mappen
Letzter Beitrag | Erster ungelesener Beitrag Druckvorschau | Thema zu Favoriten hinzufügen

Antwort erstellen
Zum Ende der Seite springen  

Regex Mapper: Groups eines Matches in Properties eines Objekts mappen

 
Autor
Beitrag « Vorheriges Thema | Nächstes Thema »
Sarc
myCSharp.de-Mitglied

Dabei seit: 29.09.2008
Beiträge: 417
Entwicklungsumgebung: VS 2012


Sarc ist offline

Regex Mapper: Groups eines Matches in Properties eines Objekts mappen

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Extension-Method zur Regex-Klasse, um anhand der Gruppennamen passende Objekte zu erzeugen

Hier erstmal das Snippet. Ein Beispiel folgt weiter unten.

C#-Code:
public static class RegexExtensions
{
    public static IEnumerable<T> Matches<T>(this Regex regex, string input, ICustomConverter<T> converter = null) where T : class, new()
    {
        if (typeof(T) == typeof(object))
            return MatchesDynamic<T>(regex, input, converter);
        else
            return MatchesDefault<T>(regex, input, converter);
    }

    private static IEnumerable<T> MatchesDefault<T>(this Regex regex, string input, ICustomConverter<T> converter = null) where T : class, new()
    {
        var bindingFlags = BindingFlags.IgnoreCase | BindingFlags.SetProperty | BindingFlags.Public | BindingFlags.Instance;
        var t = typeof(T);

        foreach (Match m in regex.Matches(input).OfType<Match>().Where(f => f.Success))
        {
            T itm = new T();

            for (int i = 1; i < m.Groups.Count; ++i)
            {
                var groupName = regex.GroupNameFromNumber(i);

                if (converter == null || !converter.Convert(groupName, m.Groups[i], itm))
                {
                    var pi = t.GetProperty(groupName, bindingFlags);

                    if (pi != null)
                        pi.SetValue(itm, Convert.ChangeType(m.Groups[i].Value, pi.PropertyType), null);
                }
            }

            yield return itm;
        }
    }

    private static IEnumerable<T> MatchesDynamic<T>(this Regex regex, string input, ICustomConverter<T> converter = null) where T : class, new()
    {
        var groupNames = regex.GetGroupNames();

        foreach (Match m in regex.Matches(input).OfType<Match>().Where(f => f.Success))
        {
            dynamic itm = new DynamicResult(m, groupNames);

            for (int i = 1; i < m.Groups.Count; ++i)
            {
                var groupName = regex.GroupNameFromNumber(i);

                if (converter != null)
                    converter.Convert(groupName, m.Groups[i], itm);
            }

            yield return itm;
        }
    }

    public interface ICustomConverter<T>
    {
        // Liefert true, falls manuell konvertiert wurde, ansonsten false
        bool Convert(string groupName, Group group, T item);
    }

    private sealed class DynamicResult : DynamicObject
    {
        private Dictionary<string, string> values;

        public DynamicResult(Match match, string[] groupNames)
        {
            this.values = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);

            foreach (var grpName in groupNames)
                this.values.Add(grpName, match.Groups[grpName].Value);
        }

        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
            string grpValue = null;

            result = this.values.TryGetValue(binder.Name, out grpValue) ? grpValue : null;
            return result != null;
        }

        public override bool TrySetMember(SetMemberBinder binder, object value)
        {
            this.values[binder.Name] = value.ToString();
            return true;
        }
    }
}

Hat man beispielsweise Zeichenfolgen die die Grundlage für bestimmte Objekte darstellen, so kann man diese automatisch mappen lassen.
Das Beispiel zeigt ein Regex-Muster für Personendaten und die entsprechende Person-Klasse.
Die Namen der Regex-Gruppen müssen den Properties der Klasse entsprechen. Falls dies nicht möglich sein sollte oder man in das Mapping eingreifen möchte,
so kann ein ICustomConverter-Objekt übergeben werden.
Die Verwendung von "regexCustomConverter" zeigt dies beispielhaft.
Ausserdem gibt es noch eine dynamische Variante, indem man als generischen Parameter 'dynamic' übergibt.

C#-Code:
static class Program
{
    static void Main(string[] args)
    {
        var regex = new Regex(@"(?<vorname>\w+)\s+(?<nachname>\w+)\s+\((?<age>\d+)\)");
        var regexCustomConverter = new Regex(@"(?<vorname>\w+)\s+(?<nachname>\w+)\s+\((?<age>\d+)\)\s+\((?<gender>[a-z])\)");

        // "Normale" Verwendung
        foreach (var p in regex.Matches<Person>("Max Mustermann (23)"))
        {
            Console.WriteLine(p.Vorname);
            Console.WriteLine(p.Nachname);
            Console.WriteLine(p.Age);
        }

        Console.WriteLine();

        // Verwendung eines CustomConverters
        foreach (var p in regexCustomConverter.Matches<Person>("Max Mustermann (23) (m)", new PersonConverter()))
        {
            Console.WriteLine(p.Vorname);
            Console.WriteLine(p.Nachname);
            Console.WriteLine(p.Age);
            Console.WriteLine(p.Gender);
        }

        Console.WriteLine();

        // Verwendung mittels dynamic
        foreach (var p in regex.Matches<dynamic>("Max Dynamic (23)"))
        {
            Console.WriteLine(p.Vorname);
            Console.WriteLine(p.Nachname);
            Console.WriteLine(p.Age);
        }

        Console.WriteLine();

        // Verwendung mittels dynamic (und Converter)
        foreach (var p in regexCustomConverter.Matches<dynamic>("Max Dynamic (23) (f)", new DynamicConverter()))
        {
            Console.WriteLine(p.Vorname);
            Console.WriteLine(p.Nachname);
            Console.WriteLine(p.Age);
            Console.WriteLine(p.Gender);
        }

        Console.ReadLine();
    }

    public class PersonConverter : RegexExtensions.ICustomConverter<Person>
    {
        public bool Convert(string groupName, Group group, Person item)
        {
            if (string.Equals("gender", groupName))
            {
                item.Gender = string.Equals(group.Value, "m") ? Gender.Male : Gender.Female;
                return true;
            }

            return false;
        }
    }

    public class DynamicConverter : RegexExtensions.ICustomConverter<object>
    {
        public bool Convert(string groupName, Group group, object item)
        {
            dynamic d = item;

            if (string.Equals("gender", groupName))
            {
                d.Gender = string.Equals(group.Value, "m") ? Gender.Male : Gender.Female;
                return true;
            }

            return false;
        }
    }

}

public class Person
{
    public string Vorname { get; set; }
    public string Nachname { get; set; }
    public int Age { get; set; }
    public Gender Gender { get; set; }
}

public enum Gender
{
    Male,
    Female
}

Viel Spass damit!

Schlagwörter: Regex, Mapper, Mapping

Dieser Beitrag wurde 1 mal editiert, zum letzten Mal von Sarc am 01.05.2011 11:41.

01.05.2011 00:43 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
dN!3L dN!3L ist männlich
myCSharp.de-Poweruser/ Experte

avatar-2985.png


Dabei seit: 13.08.2004
Beiträge: 2.891


dN!3L ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Schick.
Du könntest T noch auf Referenztypen einschränken, da der Code nicht für Werttypen funktioniert (oder du passt den Code noch für Werttypen an :) ).
01.05.2011 01:35 Beiträge des Benutzers | zu Buddylist hinzufügen
Sarc
myCSharp.de-Mitglied

Dabei seit: 29.09.2008
Beiträge: 417
Entwicklungsumgebung: VS 2012

Themenstarter Thema begonnen von Sarc

Sarc ist offline

Beitrag: beantworten | zitieren | editieren | melden/löschen       | Top

Du hast natürlich Recht. Ich habe es jetzt mal auf Referenztypen beschränkt.

Ausserdem habe ich noch eine dynamische Variante hinzugefügt.
01.05.2011 11:42 E-Mail | Beiträge des Benutzers | zu Buddylist hinzufügen
Baumstruktur | Brettstruktur       | Top 
myCSharp.de | Forum Der Startbeitrag ist älter als 9 Jahre.
Der letzte Beitrag ist älter als 9 Jahre.
Antwort erstellen


© Copyright 2003-2020 myCSharp.de-Team | Impressum | Datenschutz | Alle Rechte vorbehalten. | Dieses Portal verwendet zum korrekten Betrieb Cookies. 27.05.2020 08:49