こんにちは。
開発担当のお猿です。
年末年始にVB.NETでJSONを楽にデコードしたいな~と思いましたが、中々良いものがないので作ってみました。
今回はJSON文字列をVB上にハードコーディングする際、ダブルクォーテーションがとっても邪魔なので、ダブルクォーテーションが無くても読み込めるように調整してみました。
※「スペース」「カンマ」「コロン」を文字列として格納したいときは、ダブルクォーテーションを使用して書かないとならないです。
上手いこと活用出来たらよいかと思います。
(ソースの解析に関しては、また気が向いたときに記載したいと思います。)
Imports System.Text
Public Class JsonClass
Private _baseJson As String ' デコード対象文字列
Private _outOfChar As New Collection ' Jsonからの除外文字
Private _replaceTxt As New Hashtable ' Jsonエスケープ文字
''' <summary>
''' デコードしたJSON
''' </summary>
''' <returns>String</returns>
Public ReadOnly Property BaseJson As String
Get
Return _baseJson
End Get
End Property
''' <summary>
''' Jsonからの除外文字
''' </summary>
''' <returns>Collection</returns>
Public Property OutOfChar As Collection
Set(value As Collection)
_outOfChar = value
End Set
Get
Return _outOfChar
End Get
End Property
''' <summary>
''' Jsonエスケープ文字
''' </summary>
''' <returns>Hashtable</returns>
Public Property ReplaceTxt As Hashtable
Set(value As Hashtable)
_replaceTxt = value
End Set
Get
Return _replaceTxt
End Get
End Property
''' <summary>
''' コンストラクタ
''' </summary>
Public Sub New()
' Jsonからの除外文字
_outOfChar.Add(vbCrLf)
_outOfChar.Add(vbCr)
_outOfChar.Add(vbLf)
_outOfChar.Add(vbTab)
_outOfChar.Add(vbVerticalTab)
_outOfChar.Add(vbFormFeed)
_outOfChar.Add(vbNewLine)
_outOfChar.Add(vbNullChar)
_outOfChar.Add(vbNullString)
_outOfChar.Add(" ")
_outOfChar.Add(" ")
' Jsonエスケープ文字
_replaceTxt("\""") = """"
_replaceTxt("\\") = "\"
_replaceTxt("\/") = "/"
_replaceTxt("\n") = vbCr
_replaceTxt("\r") = vbLf
_replaceTxt("\t") = vbTab
End Sub
''' <summary>
''' Jsonをデコード
''' </summary>
''' <param name="targetString">対象文字列</param>
''' <returns>Object</returns>
Public Function JsonDecode(ByVal targetString As String) As Object
Try
' デコード対象文字列
_baseJson = Trim(targetString)
' 改行・スペースを削除する
Dim tChr As String ' 調査対象文字
Dim stFlg As Boolean = False ' 除外文字数フラグ
For i As Integer = 0 To _baseJson.Length - 1 Step 1
' 調査対象文字
tChr = _baseJson(i)
' 除外文字を除外する
stFlg = True
For Each oChr As String In _outOfChar
If tChr = oChr Then
stFlg = False
Exit For
End If
Next
If stFlg = True Then
_baseJson = _baseJson.Substring(i, _baseJson.Length - i)
Exit For
End If
Next i
' Json解析開始
If Left(_baseJson, 1) = "[" Then
' 配列の場合
Return RecursiveObject(0, "[", 0)
Else
' 何もない場合、オブジェクトとみなす
Return RecursiveObject(0, "{", 0)
End If
Catch ex As Exception
Throw
End Try
End Function
''' <summary>
''' 再起処理でオブジェクト(配列)毎にデータを取得する
''' </summary>
''' <param name="stPoint">開始位置</param>
''' <param name="chkType">オブジェクトタイプ</param>
''' <param name="resPoint">再起戻り値用</param>
''' <returns>Object</returns>
Private Function RecursiveObject(ByVal stPoint As Integer,
ByVal chkType As String,
ByRef resPoint As Integer) As Object
Try
Dim i, j, k As Integer ' ループ用
Dim tChr As String ' 調査対象文字
Dim isTxtFlg As Boolean = False ' 文字列中フラグ
Dim resObj As Object = Nothing ' 戻り値格納用
Dim keySt As Integer = stPoint + 1 ' key開始位置
Dim valSt As Integer = stPoint + 1 ' value開始位置
Dim keyData As String = "" ' keyデータ
Dim objCount As Integer = 0 ' オブジェクトキーなしの番号
Dim chkCommaFlg As Boolean ' カンマ時のフラグ
Dim dataObj As Object = Nothing ' 今回の処理の型
If chkType = "{" Then
dataObj = New Hashtable
ElseIf chkType = "[" Then
dataObj = New ArrayList
End If
For i = stPoint + 1 To _baseJson.Length - 1 Step 1 ' 2文字目から取得
' 調査対象文字
tChr = _baseJson(i)
' 文字がダブルクォーテーションの場合、フラグをスイッチする
If tChr = """" Then
isTxtFlg = Not isTxtFlg
End If
' 文字列フラグが外れている場合
If isTxtFlg = False Then
' 再起処理チェック
If tChr = "{" Then
' 再起処理(オブジェクト)
resObj = RecursiveObject(i, "{", j)
i = j + 1
tChr = _baseJson(i)
ElseIf tChr = "[" Then
' 再起処理(配列)
resObj = RecursiveObject(i, "[", j)
i = j + 1
tChr = _baseJson(i)
End If
' その他jsonキー文字列
If tChr = ":" Then
If chkType = "{" Then
' オブジェクトの場合のキーとの切り離し
keyData = _baseJson.Substring(keySt, i - keySt)
keySt = i + 1
valSt = i + 1
End If
ElseIf tChr = "}" Then
If chkType = "{" Then
' 処理が閉じた場合
Call InputObjectData(i, CType(dataObj, Hashtable), resObj, keyData, objCount, keySt, valSt)
'データを戻す
resPoint = i
Return dataObj
End If
ElseIf tChr = "]" Then
If chkType = "[" Then
' 処理が閉じた場合
Call InputArrayData(i, CType(dataObj, ArrayList), resObj, valSt)
'データを戻す
resPoint = i
Return dataObj
End If
ElseIf i = _baseJson.Length - 1 Then
' 最後まで来た場合
If chkType = "{" Then
Call InputObjectData(i, CType(dataObj, Hashtable), resObj, keyData, objCount, keySt, valSt)
'データを戻す
Return dataObj
ElseIf chkType = "[" Then
Call InputArrayData(i, CType(dataObj, ArrayList), resObj, valSt)
'データを戻す
Return dataObj
End If
ElseIf tChr = "," Then
' 次が閉じの場合、""になるのを防ぐ
For k = i + 1 To _baseJson.Length - 1 Step 1
' 次の文字列が除外文字でない場合のみ進む
chkCommaFlg = False
For Each oChr As String In _outOfChar
If _baseJson(k) = oChr Then
chkCommaFlg = True
Exit For
End If
Next
If chkCommaFlg = False Then
If _baseJson(k) <> "}" AndAlso _baseJson(k) <> "]" Then
' 次の文字が}]でなければ登録する
If chkType = "{" Then
' オブジェクトを登録する
Call InputObjectData(i, CType(dataObj, Hashtable), resObj, keyData, objCount, keySt, valSt)
ElseIf chkType = "[" Then
' 配列を登録する
Call InputArrayData(i, CType(dataObj, ArrayList), resObj, valSt)
End If
End If
Exit For
End If
Next k
End If
End If
Next i
Return Nothing
Catch ex As Exception
Throw
End Try
End Function
''' <summary>
''' オブジェクトにデータを格納する
''' </summary>
''' <param name="i">現在の桁文字位置</param>
''' <param name="dataObj">最終的なオブジェクト</param>
''' <param name="resObj">再起処理からのオブジェクト</param>
''' <param name="keyData">キー名</param>
''' <param name="objCount">キーが存在しない場合のカウント</param>
''' <param name="keySt">キー開始位置</param>
''' <param name="valSt">データ開始位置</param>
Private Sub InputObjectData(ByVal i As Integer,
ByRef dataObj As Hashtable,
ByRef resObj As Object,
ByRef keyData As String,
ByRef objCount As Integer,
ByRef keySt As Integer,
ByRef valSt As Integer)
Try
If resObj Is Nothing Then
' 再起からの戻りでない場合
If keyData = "" Then
' キーなしデータを入れる
dataObj.Add(objCount, TrimTxtData(_baseJson.Substring(valSt, i - valSt)))
objCount += 1
Else
' キーありデータを入れる
dataObj.Add(TrimTxtData(keyData), TrimTxtData(_baseJson.Substring(valSt, i - valSt)))
End If
Else
' 再起からの戻りの場合
If keyData = "" Then
' キーなしデータを入れる
dataObj.Add(objCount, resObj)
objCount += 1
Else
' キーありデータを入れる
dataObj.Add(TrimTxtData(keyData), resObj)
End If
End If
keyData = "" ' キーデータ初期化
resObj = Nothing ' 再起データ初期化
keySt = i + 1 ' key開始場所初期化
valSt = i + 1 ' val開始場所初期化
Catch ex As Exception
Throw
End Try
End Sub
''' <summary>
''' 配列にデータを格納する
''' </summary>
''' <param name="i">現在の桁文字位置</param>
''' <param name="dataObj">最終的なオブジェクト</param>
''' <param name="resObj">再起処理からのオブジェクト</param>
''' <param name="valSt">データ開始位置</param>
Private Sub InputArrayData(ByVal i As Integer,
ByRef dataObj As ArrayList,
ByRef resObj As Object,
ByRef valSt As Integer)
Try
If resObj Is Nothing Then
' 再起からの戻りでない場合
dataObj.Add(TrimTxtData(_baseJson.Substring(valSt, i - valSt)))
Else
' 再起からの戻りの場合
dataObj.Add(resObj)
End If
resObj = Nothing ' 再起データ初期化
valSt = i + 1 ' val開始場所初期化
Catch ex As Exception
Throw
End Try
End Sub
''' <summary>
''' ダブルクォーテーションと先頭・最後の改行を除外する
''' </summary>
''' <param name="targetTxt">対象文字列</param>
''' <returns>string</returns>
Private Function TrimTxtData(ByVal targetTxt As String) As String
Try
' 対象をTrim
Dim tmpData As String = Trim(targetTxt)
' 不要な文字列を削除
Dim isTxtFlg As Boolean = False ' 文字列中フラグ
Dim tChr As String = "" ' 対象文字
Dim moveFlg As Boolean = False ' 移動フラグ
Dim res As New StringBuilder ' 戻り値用
' JSONから除外文字列を削除
For i As Integer = 0 To tmpData.Length - 1 Step 1 ' 1文字ずつループ
' 調査対象文字
tChr = tmpData(i)
' 文字がダブルクォーテーションの場合、フラグをスイッチする
If tChr = """" Then
isTxtFlg = Not isTxtFlg
End If
If isTxtFlg = False Then
' 文字列フラグが折れている場合
' 除外文字列を除いてデータを格納する
moveFlg = False
For Each oChr As String In _outOfChar
If tChr = oChr Then
moveFlg = True
Exit For
End If
Next
If moveFlg = False Then
If tChr <> "," Then
res.Append(tChr)
End If
End If
Else
' 文字列フラグが立っている場合
res.Append(tChr)
End If
Next i
tmpData = res.ToString
' ダブルクォーテーションを削除
If Left(tmpData, 1) = """" AndAlso Right(tmpData, 1) = """" Then
tmpData = tmpData.Substring(1, Len(tmpData) - 2)
End If
' Json仕様上のエスケープ文字置換
If tmpData <> "" Then
For Each repTxt As DictionaryEntry In _replaceTxt
tmpData = Replace(tmpData, CType(repTxt.Key, String), CType(repTxt.Value, String))
Next
End If
Return tmpData
Catch ex As Exception
Throw
End Try
End Function
End Class
基本的に戻り値は
配列→ArrayList。
オブジェクト→Hashtable
で戻るように作っています。
一応、↑のクラスを使用した読み込みのサンプルとしてダミーのJSONを生成してくれるサイトを置いておきますね。
基本的なパターンは網羅しているかと思いますが、バグ等が見つかった場合はご連絡いただけると幸いです。