Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Easy Document in VisualBasic

0.00/5 (No votes)
20 May 2016 1  
Generate/Parsing common data document just one function

Introduction

After downloads, add project reference to:

Microsoft.VisualBasic.Architecture.Framework/+Microsoft.VisualBasic.Architecture.Framework.vbproj

For enabling the Csv document serialization feature, refer to project:

DocumentFormats/VB_DataFrame/+VB_Tabular.DataFrame.vbproj

Or refer to the DLL if you are downloading from nuget:

Microsoft.VisualBasic.Architecture.Framework_v3.0_22.0.76.201__8da45dcd8060cc9a.dll

Microsoft.VisualBasic.DocumentFormat.Csv.dll

Code Usage

All of the example test code can be download from here.

Here are some of the data objects that we want to save to file and read object from the file. In Visual Basic, there are two document formats for the storage of simple document (*.ini, *.Csv), and 3 types of document format (*.json, *.Xml, *.dat) for the storage of complex object automatically. In this document format guidelines, we want to introduce how easily that saves object instance in the Visual Basic program.

<IniMapIO("#/test.ini")>
 Public Class Profiles
     Public Property Test As TestBin
 End Class

<ClassName("JSON")>
<Serializable> Public Class TestBin
    <DataFrameColumn> Public Property Property1 As String
    <DataFrameColumn> Public Property D As Date
    <DataFrameColumn> Public Property n As Integer
    <DataFrameColumn> Public Property f As Double

    Public Shared Function inst() As TestBin
        Return New TestBin With {
            .D = Now,
            .f = RandomDouble(),
            .n = RandomDouble() * 1000,
            .Property1 = NetResponse.RFC_UNKNOWN_ERROR.GetJson
        }
    End Function
End Class

First of all, we define an object for the test example in this article:

Dim a As TestBin = TestBin.inst  ' Init test data

INI

There are two Win32 APIs that were used for ini profile file data read and write:

''' <summary>
''' Write a string value into a specific section in a specific ini profile.
''' (在初始化文件指定小节内设置一个字串)
''' </summary>
''' <param name="section">
''' <see cref="String"/>,要在其中写入新字串的小节名称。这个字串不区分大小写
''' </param>
''' <param name="key">
''' <see cref="String"/>,要设置的项名或条目名。这个字串不区分大小写。
'''<see cref="vbNullString"/>可删除这个小节的所有设置项
''' </param>
''' <param name="val">
''' <see cref="String"/>,指定为这个项写入的字串值。用<see cref="vbNullString"/>表示删除这个项现有的字串
''' </param>
''' <param name="filePath">
''' <see cref="String"/>,初始化文件的名字。如果没有指定完整路径名,则windows会在windows目录查找文件。
''' 如果文件没有找到,则函数会创建它</param>
''' <returns>Long,非零表示成功,零表示失败。会设置<see cref="GetLastErrorAPI.GetLastError()"/></returns>
<DllImport("kernel32")>
Public Shared Function WritePrivateProfileString(section As String,
                                                 key As String,
                                                 val As String,
                                                 filePath As String) As Long
End Function

''' <summary>
''' 为初始化文件中指定的条目取得字串
''' </summary>
''' <param name="section">
''' String,欲在其中查找条目的小节名称。这个字串不区分大小写。如设为vbNullString,就在lpReturnedString
''' 缓冲区内装载这个ini文件所有小节的列表。
''' </param>
''' <param name="key">
''' String,欲获取的项名或条目名。这个字串不区分大小写。如设为vbNullString,就在lpReturnedString
''' 缓冲区内装载指定小节所有项的列表
''' </param>
''' <param name="def">String,指定的条目没有找到时返回的默认值。可设为空("")</param>
''' <param name="retVal">String,指定一个字串缓冲区,长度至少为nSize</param>
''' <param name="size">Long,指定装载到lpReturnedString缓冲区的最大字符数量</param>
''' <param name="filePath">
''' String,初始化文件的名字。如没有指定一个完整路径名,windows就在Windows目录中查找文件
''' </param>
''' <returns>
''' Long,复制到lpReturnedString缓冲区的字节数量,其中不包括那些NULL中止字符。如lpReturnedString
''' 缓冲区不够大,不能容下全部信息,就返回nSize-1(若lpApplicationName或lpKeyName为NULL,则返回nSize-2)
''' </returns>
<DllImport("kernel32")>
Public Shared Function GetPrivateProfileString(section As String,
                                               key As String,
                                               def As String,
                                               retVal As StringBuilder,
                                               size As Integer,
                                               filePath As String) As Integer
End Function

And the wrapper for the ini data serialization and deserialization is already been developed for the Class object in the VisualBasic. First, you just need to import two namespaces, and then let's see how simple it is:

Imports Microsoft.VisualBasic.ComponentModel.DataSourceModel
Imports Microsoft.VisualBasic.ComponentModel.Settings.Inf

Assuming that you have a TestBin type simple object, then you want to write this object as the program profile ini file, so that you just need using WriteClass function, if your object just stands for a section in the ini profile file.

Call New Profiles With {.Test = a}.WriteProfile  ' Write profile file data
Call a.WriteClass("./test2.ini")                 ' Write ini section data.
a = Nothing
a = "./test2.ini".LoadIni(Of TestBin)                        ' Load ini section data
Dim pp As Profiles = "./test2.ini".LoadProfile(Of Profiles)  ' Load entire ini file

And in this example, the WriteClass function produces the ini profile data as:

[JSON]
Property1={"_BufferLength":8,"_ChunkBuffer":[72,84,84,80,47,53,50,48],_
"_Protocol":520,"_ProtocolCategory":0,"_uid":0}
D=5/20/2016 3:40:27 PM
n=330
f=0.33

NOTE: The profile key in the ini file should be decorating with <DataFrameColumn> attribute, and using ClassName attribute on the Class object definition, can makes tweaks on your section name and allows some identifier illegal character in VisualBasic is also able to be used as the section name, example is a section name is "test-section", the character - is illegal in the VB identifier, so that you just need using this attribute decorated as <ClassName("test-section")>, the same of the usage of <DataFrameColumn> attribute can be applied on the property.

Structure Binary

In VisualBasic, there is a BinaryFormatter that can be used for the binary serialization, all you needs to do just imports a namespace Microsoft.VisualBasic.Serialization.BinaryDumping.StructFormatter, then you get two extension methods for the serialization and deserialization.

Call a.Serialize("./test.dat")   ' test on the binary serialization
a = Nothing
a = "./test.dat".Load(Of TestBin)

StructFormatter.Serialize for saving any object as a binary file, and StructFormatter.Load(of T) function for loading any object from a binary file.

In fact, except the BinaryFormatter class, there is another method that can be used for the binary serialization, by usingMarshal.StructureToPtr and Marshal.PtrToStructure function and combine with Marshal.Copy for memory data copy, that we can serialize any structure object into binary stream, in theoretical, and from the test it actually works, but there are some problems in the serialization on the String value (as the String type is a reference type, so that when performance this serialization work, it actually serializes the memory address of your string, so that this function just works perfectly on the Integer, Long, Short, Enum, etc., value types.):

' Summary:
'     [Supported in the .NET Framework 4.5.1 and later versions] Marshals data from
'     a managed object of a specified type to an unmanaged block of memory.
'
' Parameters:
'   structure:
'     A managed object that holds the data to be marshaled. The object must be a structure
'     or an instance of a formatted class.
'
'   ptr:
'     A pointer to an unmanaged block of memory, which must be allocated before this
'     method is called.
'
'   fDeleteOld:
'     true to call the 
'     System.Runtime.InteropServices.Marshal.DestroyStructure``1(System.IntPtr)
'     method on the ptr parameter before this method copies the data. The block must
'     contain valid data. Note that passing false when the memory block already contains
'     data can lead to a memory leak.
'
' Type parameters:
'   T:
'     The type of the managed object.
'
' Exceptions:
'   T:System.ArgumentException:
'     structure is a reference type that is not a formatted class.
<SecurityCritical>
Public Shared Sub StructureToPtr(Of T)_
   ([structure] As T, ptr As IntPtr, fDeleteOld As Boolean)

' Summary:
'     Marshals data from an unmanaged block of memory to a newly allocated managed
'     object of the specified type.
'
' Parameters:
'   ptr:
'     A pointer to an unmanaged block of memory.
'
'   structureType:
'     The type of object to be created. This object must represent a formatted class
'     or a structure.
'
' Returns:
'     A managed object containing the data pointed to by the ptr parameter.
'
' Exceptions:
'   T:System.ArgumentException:
'     The structureType parameter 
'     layout is not sequential or explicit.-or-The structureType
'     parameter is a generic type.
'
'   T:System.ArgumentNullException:
'     structureType is null.
'
'   T:System.MissingMethodException:
'     The class specified by structureType does not have an accessible default constructor.
<ComVisible(True)> <SecurityCritical>
Public Shared Function PtrToStructure(ptr As IntPtr, structureType As Type) As Object

JSON

The json format is a popular data format on the network, in my job, the d3js with VisualBasic hybrids solution required of json data,

This document format in VisualBasic needs imports of this namespace at first:

Imports Microsoft.VisualBasic.Serialization

And the json serialization extension method is based on the System Json serialization solution:

Imports System.Runtime.Serialization.Json
Imports System.Web.Script.Serialization
''' <summary>
''' Gets the json text value of the target object, the attribute 
''' <see cref="ScriptIgnoreAttribute"/> 
''' can be used for block the property which is will not serialize to the text.
''' (使用<see cref="ScriptIgnoreAttribute"/>来屏蔽掉不想序列化的属性)
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="obj"></param>
''' <returns></returns>
<Extension> Public Function GetJson(Of T)(obj As T) As String

''' <summary>
''' JSON反序列化
''' </summary>
<Extension> Public Function LoadObject(Of T)(json As String) As T

And just using these two extensions that can enable you to serialize any object into Json document and deserialize Json document for instance any object type:

Dim json As String = a.GetJson   ' JSON serialization test
a = Nothing
a = json.LoadObject(Of TestBin)
Call json.__DEBUG_ECHO

And there is another perfect fast Json serialization solution for VisualBasic: Newton.Json, but in this article, I just want to introduce the System json serialization solution as this solution does not need reference of the third-part library.

XML

Generating the XML document is very easy in the VisualBasic, just using your object's GetXml extension function, and then using function LoadXml(of T) can easily load your saved object from an XML file:

' XML test
Dim xml As String = a.GetXml   ' Convert object into Xml
Call xml.__DEBUG_ECHO
Call a.SaveAsXml("./testssss.Xml")   ' Save Object to Xml
a = Nothing
a = "./testssss.Xml".LoadXml(Of TestBin)  ' Load Object from Xml
Call a.GetXml.__DEBUG_ECHO
Simple Usage
  1. .GetXml
    • Gets the object generated XML document text. By combine using of the String.SaveTo(path) Extension function, that you can easily save the object as an XML text file.
  2. .LoadXml(of T)
    • Load object from XML

CSV

By using this serialization feature, you should import this namespace at first:

Imports Microsoft.VisualBasic.DocumentFormat.Csv

Assuming that you have a type specific collection, and you want to save this collection into CSV data file and for the data exchange with R language or d3js data visualization, so that you just need simple by using SaveTo extension function applied on your data collection, then you save successfully your data collection into a CSV data file.

Dim array As TestBin() = {a, a, a, a, a, a, a, a, a, a}   ' We have a collection of object
Call array.SaveTo("./test.Csv")    ' then we can save this collection into Csv file
array = Nothing
array = "./test.Csv".LoadCsv(Of TestBin)  ' test on load csv data
Call array.GetJson.__DEBUG_ECHO

If you want to load the CSV data to a collection, so that you just need using LoadCsv(Of T) function:

<path>.LoadCsv(Of <type>)

A bug in the Microsoft Excel CSV Parser was found in this test: The filed Property1 is a json text in this test, but the Excel parser cannot parse this field correctly.

The fields is not parsing successful in the Microsoft Excel

The fields parsing successful in this library

Additional

Read and Write text document

Read/Write text document in Visual Basic is so easy! Just one method for write text file and two methods for read text file, here is a very simple example:

Dim s As String = array.GetJson
Call s.SaveTo("./tesssss.txt")

Dim lines As String() = "./tesssss.txt".ReadAllLines()
s = "./tesssss.txt".ReadAllText
Usage

You cannot believe how easily you can do on the text document read/write! By using the system default text file read/write function, you should determine that the parent directory of your text file exists or not, or if not exist when you save your text file data, then your program will crash. But using these VisualBasic text file read/write function, you do not need to worry about this problem, the function already does it for you.

  1. <String>.SaveTo(path)
  2. <path>.ReadAllText()
  3. <path>.ReadAllLines()
Read/Write Binary Data

The usage of the binary data Byte() read/write is as easy as the same of text file read/write:

  1. Byte().FlushStream(<path>) ' Write binary data
  2. <path>.ReadBinary() ' Read binary bytes from file

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here