用原始方法解析復雜字符串,json一定要用JsonMapper麽?
轉自數據之巔原文用原始方法解析復雜字符串,json一定要用JsonMapper麽?
閱讀目錄
- 1.不規則非json字符串
- 2.鍵值對字符串分割函數
- 3.復雜Json格式的字符串
- 4.標準的json格式
- 5.總結
經常采集數據,肯定會碰到解析字符串,包括整個頁面的html,或者json以及一些不標準的json格式。。。
以前用json序列化,有時候需要實體類,有的時候沒有,比較麻煩,聽說可以用JsonMapper,解析為字典格式。不過沒用過,習慣了用最原始的方法來解析字符串,所以這裏分享幾個解析的案例。也許會有點作用。
解析字符串最常用的應該是Splite和Replace了。分割,然後替換一些引號之類的。最後組合。特別是采集的時候,經常會把html頁面中某一段要提取出來,可能很多人用正則表達式,可是不會啊,也不願意去學。那只好用這些原始的方法了,時間久了,也積累一些經驗或者函數。看看幾個例子。
1.不規則非json字符串
先看看這個例子,字符串是連在一起,沒有換行的,為了方便觀察,換行了,程序是原始在一起的:
1 2 3 |
[11101630,1532,14,‘0‘,‘0‘,3,‘2015,4,23,16,05,48‘,‘4‘,1,2,0,0],
[11101631,1532,14,‘0‘,‘0‘,3,‘2015,4,23,16,09,48‘,‘0‘,,,0,0],
[11101632,1532,14,‘0‘,‘0‘,3,‘2015,4,23,16,03,10‘,‘1‘,2,2,0,0]
|
先來分析一下這個字符串的特點,才能找到思路哦:
1.每一組數據都是在[]括號對中,每一組數據用,號分割,所以最終要形成一個數組來訪問哦。
2.每一組的數據基本都是用 , 號分割,字符串類型還有單引號 ;
3.第7個數組是一個整體,也使用,號分割,整體是字符串有引號;
4.第2組數據有空值,直接用,號分割,所以splite的時候不能去掉空值,否則數組長度不一樣,定位就亂了。
既然分析都完了,那思路呢?
1.組直接分割使用 ], 標記,然後每一組要Repalce掉 [ 和 ] 。主要是最前和最後;
2.組內分割,使用 ,號標記分割,出來之前要把單引號給 替換掉 ;不然也是作為字符串,引號也包括進去了;
3.至於那個 數組 的處理,不能過於想復雜,分割之後,直接在最後增加1個元素,將固定位置7-12的組合起來;這樣也許方便點;
4.由於空值有占位,所以每一組的長度是固定的。所以處理的時候直接根據自己想要的位置來組合。
下面看看代碼了,C#版本,相對與一行代碼,仔細看,Linq很是一個神器,真的是神奇。。。說多了都是淚,為啥就沒早點學呢:
1 2 3 4 5 6 7 8 9 10 11 |
String str = @"[11101630,1532,14,‘0‘,‘0‘,3,‘2015,4,23,16,05,48‘,‘4‘,1,2,0,0],[11101631,1532,14,‘0‘,‘0‘,3,‘2015,4,23,16,09,48‘,‘0‘,,,0,0],[11101632,1532,14,‘0‘,‘0‘,3,‘2015,4,23,16,03,10‘,‘1‘,2,2,0,0]" ;
var result = str.Split( new string [] { "]," }, StringSplitOptions.None) //先整體分割組
.Select(n => n.Replace( "[" , "" ) //以下是組內分割,並去掉其他幹擾字符
.Replace( "]" , "" )
.Replace( "\‘" , "" )
.Split( ‘,‘ ).ToList())
.Select(n => //對中間一個整體單獨提取,進行組合,單獨增加一個元素
{
n.Add(String.Format( "{0},{1},{2},{3},{4},{5}" , n[6], n[7], n[8], n[9], n[10], n[11])); return n;
}).ToList();
|
看看結果怎麽樣:
回到目錄2.鍵值對字符串分割函數
由於json數據格式都是鍵值對字符串,所以這裏特意分享一個經常用到的分割函數,不用Json組件,那就用簡單的方法做一個。這個函數來源於Newlife.Core,是X組件的重要部分。源碼部分不過多解釋,就是按規則將鍵值對直接分割保持在字典中,使用方法大家可以自己實驗一下,或者參考下面的案例,都有用到這個方法。代碼如下,為了方便使用,寫成了擴展方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 |
public static class StringHelper
{
/// <summary>拆分字符串成為名值字典</summary>
/// <param name="str">要分割字符串的</param>
/// <param name="nameValueSeparator">鍵值對的分隔符</param>
/// <param name="separators">分割字符</param>
/// <returns>鍵值對字典</returns>
public static IDictionary<String, String> SplitAsDictionary( this String str, String nameValueSeparator = "=" , params String[] separators)
{
var dic = new Dictionary<String, String>();
if (String.IsNullOrWhiteSpace(str)) return dic;
if (String.IsNullOrEmpty(nameValueSeparator)) nameValueSeparator = "=" ;
if (separators == null || separators.Length < 1) separators = new String[] { "," , ";" };
String[] ss = str.Split(separators, StringSplitOptions.RemoveEmptyEntries);
if (ss == null || ss.Length < 1) return null ;
foreach ( var item in ss)
{
Int32 p = item.IndexOf(nameValueSeparator);
// 在前後都不行
if (p <= 0 || p >= item.Length - 1) continue ;
String key = item.Substring(0, p).Trim();
dic[key] = item.Substring(p + nameValueSeparator.Length).Trim();
}
return dic;
}
}
|
上面默認的鍵值對分割符號為 = 號,根據實際情況進行修改,json格式裏面一般是:冒號比較多。
回到目錄3.復雜Json格式的字符串
上面的例子比較簡單,這次看一個稍微復雜點的,雖然可能用JsonMapper可以很輕易做到,但試一下最原始的方法吧。還是按照上面的思路,先分析字符串的特點:字符串是連在一起,沒有換行的,為了方便觀察,換行了,程序是原始在一起的:
1 2 3 4 5 6 7 |
{1074:[‘墨聯‘,‘墨聯‘,‘MEX D1‘,‘#098000‘,‘98‘],
2100:[‘美乙‘,‘美乙‘,‘USL D2‘,‘#E89B10‘,‘98‘],
1024:[‘阿甲‘,‘阿甲‘,‘ARG‘,‘#00CCFF‘,‘98‘],
1052:[‘哥倫甲‘,‘哥倫甲‘,‘COLCMA‘,‘#888500‘,‘98‘],
1028:[‘K聯賽‘,‘K聯賽‘,‘KORL‘,‘#F75000‘,‘98‘],
1297:[‘球會友誼‘,‘球會友誼‘,‘CF‘,‘#5691D8‘,‘98‘],
2085:[‘奧女甲‘,‘奧女甲‘,‘AFB‘,‘#D86220‘,‘97‘]}
|
還是先分析特點,這個格式應該是json類似的了,比較規則:
-
組與之間是使用 , 號分割;前後有{}括號對;觀察前後可以使用 ], 字符串將組分開;
-
鍵 是整數,鍵值是通過 : 號分割;
-
值是一個數組,有5個元素,通過 , 號分割
-
都有單引號,需要過濾掉;其他沒有特殊情況;
代碼解決過程:
1 2 3 4 5 6 7 |
string text = @"{1074:[‘墨聯‘,‘墨聯‘,‘MEX D1‘,‘#098000‘,‘98‘],2100:[‘美乙‘,‘美乙‘,‘USL D2‘,‘#E89B10‘,‘98‘],1024:[‘阿甲‘,‘阿甲‘,‘ARG‘,‘#00CCFF‘,‘98‘],1052:[‘哥倫甲‘,‘哥倫甲‘,‘COLCMA‘,‘#888500‘,‘98‘],1028:[‘K聯賽‘,‘K聯賽‘,‘KORL‘,‘#F75000‘,‘98‘],1297:[‘球會友誼‘,‘球會友誼‘,‘CF‘,‘#5691D8‘,‘98‘],2085:[‘奧女甲‘,‘奧女甲‘,‘AFB‘,‘#D86220‘,‘97‘]}" ;
var dic = text.Replace( "\‘" , "" ).Split( new String[]{ "]," }, StringSplitOptions.None) //先組分割
.Select(n => n.Replace( "{" , "" ).Replace( "}" , "" ) //將組裏面的幹擾字符過濾掉
.Replace( "[" , "" ).Replace( "]" , "" )
.SplitAsDictionary( ":" , "\"" ) //鍵值對處理,冒號分隔符
.ToDictionary(t => t.Key, t => t.Value.Split( ‘,‘ ) //值再次進行分割,形成數組
)).ToArray();
|
看看結果如何:
回到目錄4.標準的json格式
在實際的采集過,可能會碰到直接url,返回的就是json格式,比較標準。這裏舉個例子,如下面這個頁面鏈接:
http://d.dacai.com/zhishu/CorrectCorp.html?matchId=1899040&matchStatusId=41
點擊開後,是這個樣子:
這個應該夠復雜了,我們分析一下,篇幅較大,源碼對照鏈接:
-
每一大組數據,是用,號分割;整體也是前後大括號對{},有4個大類,鍵分布是:IndBodans,IndEvenGoals,IndGoalss,IndHalfFulls。
-
每一組的子類中,包括的都是同一個類型的數據,每一個子數據組都有N多個小的鍵值對,如MatchId,CorpId等等,使用},可以分割
分析完成之後如何解決,思路是類似的,先整體分割,使用},進行,然後每一個之類作為鍵,對值再次進行分割。。。形成字典列表。其中的引號也是需要過濾的,至於數值類型,實際用到那個的時候,都是根據key去找,再進行Convert轉換就可以了。看看源代碼:
1 2 3 4 5 6 7 |
var dicList = doc.Split( "{\"IndBodans\":[" , "],\"IndEvenGoals\":[" , //大類分割
"],\"IndGoalss\":[" , "],\"IndHalfFulls\":[" , "]}" )
.Select(n => n.Replace( "\"" , "" ).Split( "},{" ) //對每個大類處理,先過濾,再分割為子類
.Select(k => k.Replace( "{" , "" ).Replace( "}" , "" ) //對子類過濾
.SplitAsDictionary( ":" , "," )) //提取子類的鍵值對
.ToArray()
).ToList(); //列表
|
看看解析的結果如何:
註意,有元素是空值的,可以不用管。至於那幾個鍵值對數組,最終屬於哪一個大類?雖然沒有直接標記出來,但是這個問題隨便加一個判斷就可以了,因為每一個類別的鍵名稱是不一樣的,看看是否包括對應的鍵就可以確定類別。
回到目錄用原始方法解析復雜字符串,json一定要用JsonMapper麽?