⚠WARNING
ASP.NET에 대한 포스팅이 아닙니다
나가실 문은 오른쪽 하단입니다
파싱 작업이라는 것은 언제나 몇 번이라도. 적지 않는 정신력을 소모한다.
단순히 문자열을 틀에 맞추는 작업일 뿐인데,
깔끔하게 처리하기가 여간 까다롭다.
그런데도 마음속 어딘가에서는 끌리는 매력을 느낀다.
아마도 첫 직장 첫 제안으로 했던 풋풋함이 심층 깊은 골목의 한켠에 녹아들어 있다고 납득하고 싶다.
그날 그 여름날에는.. 반복적인 SQL을 입력하는 단순 노가다에 어떠한 패턴을 느껴버리고 나서, 어지간히도 자동화를 외치고 싶었다.
신입이었던 나는 정규식도, 문자열을 조작하는 메서드도 그러한 테크닉도 몰랐다. (물론 지금도 잘 모른다.)
그냥 IndexOf 로 상황에 맞는 단어가 나오면 거기에 맞게 문자열을 바꾸는 방식이었다.
학교에서는 자바라는것을 공부했는데, 직장에서는 C#이라는 것으로 일을 했기 때문이었다.
내가 자바를 공부 했을 적에는 자바 하나만으로 온 세상 코드들을 다 이해할 수 있을 거 같았기 때문에 다른 언어에는 관심이 없었다.
C#에 대해 조ー또(ちょっと)모르는 나에게 왜 그런 업무를 부여했는지 지금은 어렴풋이 알 것 같지만, 당신에는 너무 암 껏도 몰라서. 뭐라도 해야겠다는 압박감에 패기 넘치는 제안을 한 것이다.
그 일로 한 80% 이상이 자동화되어서, 더욱 패기 넘치게 된 것으로 인해 안 좋은 역사가 남긴 했지만 말이다.
그렇다
개발은 겸손이 중요하다.
그렇다
잘한다 잘한다 소리 몇 번 들으면 감당하기 힘든 업무가 쏟아지기 때문이다.
...
잡설은 여기까지 하고, 전편에 이어 파싱 하는 부분을 다음과 같이 작성하였다.
public static J parse(string str)
{
return fromString(str, 0).rt;
}
static readonly Exception syntaxErr = new ArgumentException("Syntax Error.");
static (J rt,int ix) fromString(string str, int ix)
{
void skipEmpty()
{
for (; ix < str.Length && Char.IsWhiteSpace(str[ix]); ix++) ;
if (ix == str.Length) throw syntaxErr;
}
skipEmpty();
J rt = str[ix] switch
{
'[' => new JL(),
'{' => new JO(),
_ => throw syntaxErr
};
ix++;
bool keyPhase = rt is JO;
string next()
{
skipEmpty();
if (str[ix] is '[' or '{') return ""+ str[ix];
int st = ix;
if (str[ix] == '"')
{
ix++;
for(bool esc = false; ix < str.Length; ix++)
{
var c = str[ix];
if (c == '"' && !esc) break;
esc = !esc && c == '\\';
}
if (ix == str.Length) throw syntaxErr;
ix++;
}
string ckstr = "," + (rt is JL ? "]" : keyPhase ? ":}" : "}");
ix = str.IndexOfAny((" " + ckstr).ToArray(), ix);
if (ix == -1) throw syntaxErr;
var rtstr = str[st..ix];
skipEmpty();
ix = str.IndexOfAny(ckstr.ToArray(), ix);
if (ix == -1) throw syntaxErr;
return rtstr;
}
bool endCheck(char c)
{
return (rt, c) switch
{
(JO, '}') => true,
(JL, ']') => true,
(_ , ',') => false,
_ => throw syntaxErr
};
}
for(string key = "?" ; ix < str.Length; ix++)
{
string tk = next();
if (tk.Length == 0) break;
char c = str[ix];
bool isStr = tk.StartsWith("\"");
bool isSub = tk is "[" or "{";
if (isSub)
{
if(keyPhase) throw syntaxErr;
(J child, ix) = fromString(str, ix);
rt.Add(key, child);
ix++;
tk = next();
c = str[ix];
if (tk.Length > 0) throw syntaxErr;
keyPhase = rt is JO;
if (endCheck(c)) break;
continue;
}
tk = Unescape(tk).Trim();
object value;
if (isStr) value = tk[1..^1];
else
{
int ival;
double dval;
value = tk switch
{
"null" => null,
_ when int.TryParse(tk, out ival) => ival,
_ when double.TryParse(tk, out dval) => dval,
_ => tk
};
}
if (rt is JO)
{
if(keyPhase && (!isStr || c != ':')) throw syntaxErr;
if (keyPhase) key = value.ToString();
else rt.Add(key, value);
keyPhase = !keyPhase;
if (keyPhase == false) continue;
}
else rt.Add(value);
if (endCheck(c)) break;
}
if (ix == str.Length) throw syntaxErr;
return (rt, ix);
}
보다시피, 사전에 언급했듯이 꽤 손이 많이 갔다.
그리고 테스트 소스를 작성하였다.
J j = J.O((1, J.L(1, 2, J.L(1.1, 1.2, 1.3), 4, "5")), ("k\"ey", "value"));
var jstr = j.stringify();
Console.WriteLine(jstr);
var newObj = J.parse(jstr);
Console.WriteLine(newObj.stringify());
그리고 최종 확인.
이것으로 추억과 함께한 JSON 파트를 마치도록 하겠다.
UnEscape :
2022.03.08 - [C#/예제 코드] - c# 자바스크립트 문자열 encode & decode (escape)