Skip to content

Instantly share code, notes, and snippets.

@dotNetTree
Created April 21, 2018 06:06
Show Gist options
  • Save dotNetTree/1d08fd44352afb66446fb1fd36f06efc to your computer and use it in GitHub Desktop.
Save dotNetTree/1d08fd44352afb66446fb1fd36f06efc to your computer and use it in GitHub Desktop.
a simple querystring parser with nesting support
func parse(qs: String) -> [String: Any]? {
guard let regex = try? NSRegularExpression.init(
pattern: "(?:[^\\[\\]]+)",
options: NSRegularExpression.Options(rawValue: 0)
) else {
return nil
}
return qs
.split(separator: "&")
.map { $0.split(separator: "=") }
.reduce(NSMutableDictionary()) { (accu, curr) in
// []에 대한 처리를 쉽게 하기 위해서 querystring 구성 시 separator로 사용한 &를 넣어준다.
// (keyPath 추적시 &가 나오면 Array로 판단하기 위함)
let key = String(String(curr[0]).replacingOccurrences(of: "[]", with: "[&]"))
let val = curr.count > 1 ? curr[1]: ""
let matches = regex.matches(
in: key,
options: NSRegularExpression.MatchingOptions(rawValue: 0),
range: NSRange.init(location: 0, length: key.count)
)
let keyPath = matches.map { (checkResult, _) -> String in
let idxStart = String.Index(encodedOffset: checkResult.range.location)
let idxEnd = String.Index(encodedOffset: checkResult.range.location + checkResult.range.length)
return String(key[idxStart..<idxEnd])
}
var storage: Any = accu
let count = keyPath.count
for i in 0..<count {
let cKey = keyPath[i]
// 다음 path가 있으므로 더 들어가야 함.
if let nKey = i < count - 1 ? keyPath[i+1] : nil {
switch storage {
case let _storage as NSMutableDictionary:
switch (nKey, _storage[cKey]) {
case ("&", nil): _storage[cKey] = NSMutableArray()
case (_, nil): _storage[cKey] = NSMutableDictionary()
default: break
}
storage = _storage[cKey]!
case let _storage as NSMutableArray:
switch (nKey, _storage.lastObject) {
case ("&", nil): _storage.add(NSMutableArray())
case (_, nil): _storage.add(NSMutableDictionary())
default: break
}
storage = _storage.lastObject!
default: break
}
} else { // storage에 적재
switch storage {
case let storage as NSMutableDictionary:
storage.setValue(val, forKey: cKey)
case let storage as NSMutableArray:
storage.add(val)
default: break
}
}
}
return accu
} as? [String: Any]
}
func test() {
print(
parse(qs: "param[url]=https://www.example.com&param[title]=XXX 이벤트&param[err]=&param[test][]=tesvale&param[test][]=tesvale2&param[a][][][kk]=2&param[a][][][ff]=3")
)
print(
parse(qs: "name=kang&gae=39")
)
}
/*
Optional(["param": {
a = (
(
{
ff = 3;
kk = 2;
}
)
);
err = "";
test = (
tesvale,
tesvale2
);
title = "XXX \Uc774\Ubca4\Ud2b8";
url = "https://www.example.com";
}])
Optional(["name": kang, "gae": 39])
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment