Das BGB als XML


(Verwendungsbeispiel)

Wer, aus welchen Gründen auch immer, das bürgerliche Gesetzbuch in seine Anwendung implementieren will, der wird sich über das „BGB als XML“ freuen.

Download als .zip-Archiv
Download als .xml (ca. 3,3 MB)

Dank des universellen XML-Formats kann das BGB plattform- und programmiersprachenunabhängig eingebunden werden. Der einfachheit halber enthält es kein Inhaltsverzeichnis und keine Hierarchie. Es beschränkt sich lediglich auf die Normen und handelt sich um die aktuellste Ausgabe zum Zeitpunkt der Erstellung (Juli 2012).

So schaut beispielsweise ein XML-Knoten („Norm“) aus:

    
      (1) Der Finder kann von dem Empfangsberechtigten einen Finderlohn
verlangen. Der Finderlohn beträgt von dem Werte der Sache bis zu 500
Euro fünf vom Hundert, von dem Mehrwert drei vom Hundert, bei Tieren
drei vom Hundert. Hat die Sache nur für den Empfangsberechtigten einen
Wert, so ist der Finderlohn nach billigem Ermessen zu bestimmen.
(2) Der Anspruch ist ausgeschlossen, wenn der Finder die Anzeigepflicht
verletzt oder den Fund auf Nachfrage verheimlicht.
      Neugefasst durch Bek. v. 2.1.2002 I 42
      § 971
      Finderlohn
    

Wer seine Anwendung ohnehin in C# .NET schreibt, der kann direkt meinen Deserializer und die dazugehörige Wrapperklasse verwenden:

 
[Serializable]
    public class BGB
    {
        private List<Norm> _Normen;

        public List<Norm> Normen
        {
            get { return _Normen; }
            set { _Normen = value; }
        }

        [Serializable]
        public class Norm
        {
            private string _text;

            public string Text
            {
                get { return _text; }
                set { _text = value; }
            }

            private string _appendix;

            public string Appendix
            {
                get { return _appendix; }
                set { _appendix = value; }
            }

            private string _paragraph;

            public string Paragraph
            {
                get { return _paragraph; }
                set { _paragraph = value; }
            }

            private string _title;

            public string Title
            {
                get { return _title; }
                set { _title = value; }
            }
        }

        public BGB()
        {
            _Normen = new List<Norm>();
        }

        public void AddNorm(Norm Norm)
        {
            _Normen.Add(Norm);
        }
    }

Der (De-)Serializer:

 
public class Serializer
    {
        public void SerializeToXMLString<T>(T XMLObj, Stream s, Encoding encoding, bool removeNamespace)
        {
            try
            {
                XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
                XmlTextWriter xmlSink = new XmlTextWriter(s, encoding);
                xmlSink.Formatting = Formatting.Indented;

                if (removeNamespace)
                {
                    XmlSerializerNamespaces xs = new XmlSerializerNamespaces();
                    xs.Add("", "");
                    xmlSerializer.Serialize(xmlSink, XMLObj, xs);
                }
                else
                    xmlSerializer.Serialize(xmlSink, XMLObj);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
                Console.WriteLine(ex.StackTrace);

                if (ex.InnerException != null)
                    Console.WriteLine(ex.InnerException);
            }
        }

        public void SerializeObjectToXMLFile<T>(T XMLObj, string Filename, Encoding encoding, bool removeNamespace)
        {
            FileStream fs = new FileStream(Filename, FileMode.Create);
            SerializeToXMLString<T>(XMLObj, fs, encoding, removeNamespace);
        }

        public void SerializeObjectToXMLFile<T>(T o, string Filename)
        {
            SerializeObjectToXMLFile<T>(o, Filename, Encoding.Unicode, true);
        }

        private MemoryStream GetUTF16MS(string xml)
        {
            // Encode the XML string in a UTF-8 byte array
            byte[] encodedString = Encoding.Unicode.GetBytes(xml);

            // Put the byte array into a stream and rewind it to the beginning
            MemoryStream ms = new MemoryStream(encodedString);
            ms.Flush();
            ms.Position = 0;
            return ms;
        }

        private bool DeserializeFromXMLString<T>(string XML, ref T myObject, bool allow_log)
        {
            if (XML == string.Empty)
                return false;

            XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
            MemoryStream ms = GetUTF16MS(XML);

            try
            {
                XmlReader xmlReader = XmlReader.Create(ms);
                while (xmlReader.Read())
                { }

                ms.Position = 0;
            }
            catch (Exception ex)
            {
                if (allow_log)
                    Console.WriteLine(ex.Message);

                return false;
            }

            //StringReader sr = new StringReader(XML);
            //myObject = (T)xmlSerializer.Deserialize(sr);
            myObject = (T)xmlSerializer.Deserialize(ms);

            return true;
        }

        public bool deserializeObjectByXML<T>(string data, ref T myObject, bool allow_log)
        {
            return DeserializeFromXMLString(data, ref myObject, allow_log);
        }
    }

C#: TimeSpan serialisieren

Möchte man mit einer Klasse einen TimeSpan serialisieren, muss man sich eines kleinen Wrappers behelfen, da andernfalls der TimeSpan-Wert falsch serialisiert und so beim Deserialisieren mit „00:00:00“ überschrieben wird. Das kann zu äußerst unangenehmen Nebeneffekten führen und mir ist es unbegreiflich warum TimeSpans nicht von Haus aus sauber serialisiert werden können.

Binde dazu das folgende struct in deine Klasse ein, die serialisiert werden soll:

 
        public struct TimeSpan : IXmlSerializable
        {
            private System.TimeSpan _value;

            public static implicit operator TimeSpan(System.TimeSpan value)
            {
                return new TimeSpan { _value = value };
            }

            public static implicit operator System.TimeSpan(TimeSpan value)
            {
                return value._value;
            }

            public XmlSchema GetSchema()
            {
                return null;
            }

            public void ReadXml(XmlReader reader)
            {
                _value = System.TimeSpan.Parse(reader.ReadElementContentAsString());
            }

            public void WriteXml(XmlWriter writer)
            {
                writer.WriteValue(_value.ToString());
            }
        }

Diese Wrapperklasse stammt ursprünglich hier her: , funktionierte allerdings nicht richtig bei mir, da der reader-Aufruf „ReadContentAsString()“ eine Exception warf. Die obige Klasse funktioniert bei mir fehlerfrei, auch in Verbindung mit dem Entity Framework.

Und so schaut das ganze als serialisiertes XML aus:

    <ServiceRuntimeDefinitionService>
      <SerializedServiceRuntimeDefinition>
        <SeralizedServiceRuntimeDaysList />
        <Description />
        <RuntimeName>DummyRuntime</RuntimeName>
        <Start>13:00:00</Start>
        <End>14:00:00</End>
        <ToleranceStart>00:10:00</ToleranceStart>
        <ToleranceEnd>00:10:00</ToleranceEnd>
        <DefaultAVG>00:01:00</DefaultAVG>
        <Duration>2.08:29:35.2500000</Duration>
      </SerializedServiceRuntimeDefinition>

Großhochzeit: C#, Delphi, Assembler und COM

Ein Feature das mit großer Wahrscheinlichkeit in zukünftigen Versionen von .NET noch implementiert werden wird, ist (managed/inline-) Assembler in seine Projekte einzubinden. In einigen Hochsprachen mit „echtem“ Compiler ist dies bereits seit vielen Jahren Standard: C++, TurboPascal, FreePascal, Delphi, freeBASIC, usw.

Zudem findet man in alten mächtigen Source-Code-Archiven oft bis zur Perversion optimierte Algorithmen, die der Autor bereits in Inline-ASM implementiert hat. Es wäre doch zu schade diese nicht weiter nutzen zu können…

Folgenden Code fand ich im SwissDelphiCenter:
http://www.swissdelphicenter.ch/de/showcode.php?id=2049
(Ein Instring-Algorithmus von Vanja Fuckar, 100% inline-ASM)

Delphi erlaubt es mit sehr wenig Aufwand einen Quelltext direkt als COM-Programmbibliothek zu kompilieren. Heraus kommt eine dll-Datei (hier: „InString_dll.dll“) die ich nun in mein .NET-Projekt einbinde mittels:


[System.Runtime.InteropServices.DllImport("InString_dll.dll", CallingConvention = System.Runtime.InteropServices.CallingConvention.StdCall, CharSet = System.Runtime.InteropServices.CharSet.Unicode)]
private static extern int InString(int StartPosition, string Source, string Pattern);

Aufgerufen wird diese externe statische Funktion wie üblich z.B. mit:


Console.WriteLine(InString(0, "Testautotesttesttest", "auto").ToString());

Einen Haken hat das (vor)kompilieren von Inline-Assembler: Es gibt keine Gewährleistung, dass diese Programmbibliothek auch auf anderen Prozessorarchitekturen bzw. -familien ausgeführt werden kann, als auf der sie kompiliert wurde.
Exemplarisch habe ich dafür die .NET-Anwendung auf einem AMD Athlon X2 64bit ausgeführt: der JIT-Debugger spuckt eine Exception aus, die mir eine ungültige Typenumwandlung bestätigt.
Auf einem Intel Pentium 4 HT wiederum lief der Quelltext einwandfrei.
Anmerkung dazu: Die Programmbibliothek habe ich auf einer Intel Pentium M kompiliert, die bekannterweise nicht aus der Familie der NetBurst-Architekturen stammt.

Nachtrag: Auf einem Pentium Xeon (NetBurst) lief die Anwendung ebenfalls einwandfrei.

C#.NET: Winamp-Titel auslesen

Winamp-Titel auslesen in C#

Möchte man mit C# wie in Delphi den aktuellen Winamp-Titel auslesen (siehe vorheriger Artikel), kann man sich prinzipiell den selben Methoden bedienen. Der Zugriff auf die WinAPI gestaltet sich allerdings etwas trickiger. Dieser Snippet darf privat und kommerziell verwendet werden. Ich würde mich in beiden Fällen sehr über Feedback freuen!


        [System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto)]
        public static extern IntPtr FindWindow(string strClassName, int nptWindowName);
        [System.Runtime.InteropServices.DllImport("user32.dll")]
        public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, long lParam);
        [System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)]
        static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
        [System.Runtime.InteropServices.DllImport("kernel32.dll")]
        static extern IntPtr OpenProcess(UInt32 dwDesiredAccess, Boolean bInheritHandle, UInt32 dwProcessId);
        [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
        static extern bool ReadProcessMemory(IntPtr hProcess, IntPtr lpBaseAddress, [System.Runtime.InteropServices.Out] byte[] lpBuffer, UIntPtr nSize, IntPtr lpNumberOfBytesRead);
        [System.Runtime.InteropServices.DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool CloseHandle(IntPtr handle);

        private static string GetWinampTitle()
        {
            const uint WM_USER = 0x0400;
            const uint PROCESS_ALL_ACCESS = 0x000F0000 | 0x00100000 | 0xFFF;

            uint ProcessHandle;
            byte[] dat2 = new byte[500];
            IntPtr temp = (IntPtr) 0;

            IntPtr hwndWinamp = FindWindow("Winamp v1.x", 0);
            IntPtr MPointer = SendMessage(hwndWinamp, WM_USER, SendMessage(hwndWinamp, WM_USER, (IntPtr) 0, 125), 212);
            GetWindowThreadProcessId(hwndWinamp, out ProcessHandle);
            hwndWinamp = OpenProcess(PROCESS_ALL_ACCESS,false,ProcessHandle);
            ReadProcessMemory(hwndWinamp, MPointer, dat2, (UIntPtr) 500, temp);
            CloseHandle(hwndWinamp);
            return Convert.ToString(System.Text.Encoding.ASCII.GetString(dat2));
        }

Code39 Barcodes über ASP.NET (VB) ausgeben

Code39 über ASP.NET ausgeben
Code39 über ASP.NET ausgeben

Den folgenden Quelltext habe ich vor etwa 2 Jahren in seiner ursprünglichen Form in einem VBA-Forum gefunden und an VB.NET angepasst um in einer ASP.NET-Anwendung on-the-fly Barcodes auszugeben.
Vorteil von Code39 ist seine große Druck- und Lesetoleranz. Zwischenzeitlich hatte ich den Sourcecode auch einmal in C# umgeschrieben. Dieser ist allerdings bei einem Festplattendefekt verloren gegangen.


Private Function MD_Barcode39(ByVal Barcode As String, ByVal PaintObj As Object, _
ByVal mLeft As Single, ByVal mTop As Single, _
ByVal mWidth As Single, ByVal mHeight As Single)

Dim Nbar As Single, Wbar As Single, Qbar As Single, NextBar As Single
Dim CountX As Single, CountY As Single
Dim Parts As Single, Pix As Single, BarCodePlus As String
Dim Stripes As String, BarType As String
Dim Mx As Single, my As Single, Sx As Single, Sy As Single
Const Nratio = 20, Wratio = 55, Qratio = 35

Dim g As System.Drawing.Graphics
Dim pB As New System.Drawing.SolidBrush(System.Drawing.Color.Black)
Dim pW As New System.Drawing.SolidBrush(System.Drawing.Color.White)
Dim color As System.Drawing.SolidBrush

'Get control size and location properties.
Sx = mLeft
Sy = mTop
Mx = mWidth
my = mHeight

g = PaintObj

'Calculate actual and relative pixels values.
Parts = (Barcode.Length + 2) * ((6 * Nratio) + (3 * Wratio) + (1 * Qratio))
Pix = (Mx / Parts)
Nbar = (20 * Pix)
Wbar = (55 * Pix)
Qbar = (35 * Pix)

'Initialize bar index and color.
NextBar = Sx
color = pW

'Pad each end of string with start/stop characters.
BarCodePlus = "*" &amp; Barcode.ToUpper &amp; "*"

'Walk through each character of the barcode contents.
For CountX = 0 To BarCodePlus.Length - 1
'Get Barcode 1/0 string for indexed character.
Stripes = MD_BC39(BarCodePlus.Substring(CountX, 1))
For CountY = 0 To 8
'For each 1/0, draw a wide/narrow bar.
BarType = Stripes.Substring(CountY, 1)

'Toggle the color (black/white).
If color Is pW Then
color = pB
Else
color = pW
End If

Select Case BarType
Case "1"
'Draw a wide bar.
g.FillRectangle(color, NextBar, Sy, Wbar + NextBar, my + Sy)
NextBar = NextBar + Wbar
Case "0"
'Draw a narrow bar.
g.FillRectangle(color, NextBar, Sy, Nbar + NextBar, my + Sy)
NextBar = NextBar + Nbar
End Select
Next CountY

'Toggle the color (black/white).
If color Is pW Then
color = pB
Else
color = pW
End If

'Draw intermediate "quiet" bar.
g.FillRectangle(color, NextBar, Sy, Qbar + NextBar, my + Sy)
NextBar = NextBar + Qbar
Next CountX
End Function

Function MD_BC39(ByVal CharCode As String) As String
Dim BC39(90) As String

BC39(32) = "011000100" ' space
BC39(36) = "010101000" ' $
BC39(37) = "000101010" ' %
BC39(42) = "010010100" ' * Start/Stop
BC39(43) = "010001010" ' +
BC39(45) = "010000101" ' |
BC39(46) = "110000100" ' .
BC39(47) = "010100010" ' /
BC39(48) = "000110100" ' 0
BC39(49) = "100100001" ' 1
BC39(50) = "001100001" ' 2
BC39(51) = "101100000" ' 3
BC39(52) = "000110001" ' 4
BC39(53) = "100110000" ' 5
BC39(54) = "001110000" ' 6
BC39(55) = "000100101" ' 7
BC39(56) = "100100100" ' 8
BC39(57) = "001100100" ' 9
BC39(65) = "100001001" ' A
BC39(66) = "001001001" ' B
BC39(67) = "101001000" ' C
BC39(68) = "000011001" ' D
BC39(69) = "100011000" ' E
BC39(70) = "001011000" ' F
BC39(71) = "000001101" ' G
BC39(72) = "100001100" ' H
BC39(73) = "001001100" ' I
BC39(74) = "000011100" ' J
BC39(75) = "100000011" ' K
BC39(76) = "001000011" ' L
BC39(77) = "101000010" ' M
BC39(78) = "000010011" ' N
BC39(79) = "100010010" ' O
BC39(80) = "001010010" ' P
BC39(81) = "000000111" ' Q
BC39(82) = "100000110" ' R
BC39(83) = "001000110" ' S
BC39(84) = "000010110" ' T
BC39(85) = "110000001" ' U
BC39(86) = "011000001" ' V
BC39(87) = "111000000" ' W
BC39(88) = "010010001" ' X
BC39(89) = "110010000" ' Y
BC39(90) = "011010000" ' Z

Return BC39(Asc(CharCode))
End Function

Möchte man wie ich die Barcodes in einer ASP.NET-Anwendung ausgeben, kann folgender (schlampiger) Code als kleine Anregung verwendet werden:


Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
Dim barcode As String = Request("barcode")
If Len(barcode) = 0 Then barcode = ""
Dim height As String = Request("height")
If (Len(height) = 0) Or (height > 768) Then height = 75
Dim width As String = Request("width")
If (Len(width) = 0) Or (width > 1024) Then width = 250
Dim plain As Boolean = True
Dim xh, xw As Integer
If Len(Request("plain")) <> 0 Then
If Request("plain") = 1 Then
plain = True
xw = width
xh = height
height = height + 15
Else
plain = False
End If
End If

Dim bmp As New Bitmap(width, height, PixelFormat.Format24bppRgb)

Dim g As Graphics = Graphics.FromImage(bmp)
MD_Barcode39(barcode, g, 0, 0, width, height)

If plain = True Then
Dim dispText As String = barcode
Dim dispFont As New Font("arial", 15, FontStyle.Regular, GraphicsUnit.Pixel)

g.FillRectangle(Brushes.White, 0, xh, xw, 15)

g.DrawString(dispText, dispFont, Brushes.Black, New PointF(0, xh))
End If

bmp.Save(Response.OutputStream, ImageFormat.Jpeg)
bmp.Dispose()
Response.End()
End Sub

In die @Page-Direktive muss noch ContentType=“image/jpeg“ hinzugefügt werden:


<%@ Page Language="VB" ContentType="image/jpeg" AutoEventWireup="true" CodeFile="barcode.aspx.vb" Inherits="barcode" %>

Aufrufen kann man das Dokument anschließend über den lokalen Webserver oder einen (externen) IIS. Der Aufruf vom Bild oben lautet:

http://localhost:12384/barcode.aspx?barcode=1234567890&width=400&plain=1