빌드노트
ConvertTo-Json 결과에서 중첩 객체가 문자열로 바뀌는 이유
ConvertTo-Json의 기본 깊이를 넘으면 중첩 객체가 문자열로 바뀌어도 JSON 파싱은 성공할 수 있다. PowerShell 7.6.6에서 경고·재파싱 타입·말단 값을 비교하고 필요한 깊이를 정한다.
PowerShell의 ConvertTo-Json으로 만든 JSON이 정상적으로 파싱되는데, 원래 객체였던 속성이 "@{limit=3}" 같은 문자열로 바뀔 수 있다. 먼저 JSON을 만드는 줄의 -Depth와 깊이 초과 경고를 확인한다. 파싱 성공만으로는 원래 중첩 구조가 보존됐다고 판단할 수 없다.
ConvertTo-Json 공식 문서 (새 탭에서 열림)에 따르면 기본 깊이는 2다. 필요한 객체가 그보다 깊으면 깊이를 명시하고, 다시 파싱한 속성의 타입과 말단 값을 확인한다. 아래 합성 객체는 -Depth 3에서 필요한 구조를 보존했다.
기본값에서도 JSON 파싱은 성공한다
다음은 Windows의 PowerShell 7.6.6에서 실행한 예다. PowerShell 7 터미널을 pwsh -NoProfile로 열어 실행할 수 있으며, 추가 패키지나 파일은 필요 없다. 실제 설정을 읽거나 API로 보내지 않고, retry.limit가 3인 작은 객체만 만든다.
아래 코드는 기본 깊이로 만든 JSON과 경고 수를 출력하고, 다시 읽은 retry의 타입과 limit의 누락 여부를 확인한다.
$payload = [pscustomobject]@{
service = [pscustomobject]@{
policy = [pscustomobject]@{
retry = [pscustomobject]@{ limit = 3 }
}
}
}
$json = ConvertTo-Json -InputObject $payload -Compress `
-WarningVariable jsonWarnings -WarningAction Continue
$copy = ConvertFrom-Json -InputObject $json
$json
$jsonWarnings.Count
$copy.service.policy.retry.GetType().FullName
$null -eq $copy.service.policy.retry.limit
한국어 환경에서 나온 출력은 다음과 같다. 경고는 JSON을 만드는 시점에 표시됐다.
경고: 직렬화가 설정된 깊이(2)를 초과하므로 결과 JSON이 잘립니다.
{"service":{"policy":{"retry":"@{limit=3}"}}}
1
System.String
True
입력의 retry는 PSCustomObject였지만, JSON에서는 따옴표로 둘러싸인 문자열이 됐다. ConvertFrom-Json은 이 문자열을 정상적으로 읽었다. 따라서 예외가 없었어도 retry.limit라는 속성에는 접근할 수 없고, 마지막 줄은 그 값이 $null임을 보여 준다. 여기서는 원래 값이 3이므로 보존 실패를 구별할 수 있다.
-WarningVariable jsonWarnings는 경고를 $jsonWarnings에 모은다. 변수 이름을 옵션에 줄 때는 $를 붙이지 않는다. -WarningAction Continue는 경고를 표시하면서 변환을 계속하도록 지정한 것이다. 이 두 옵션의 동작은 공통 매개변수 문서 (새 탭에서 열림)에서 확인할 수 있다. 경고 표시를 SilentlyContinue로 바꾼 별도 비교에서도 문자열로 바뀐 JSON은 같았고, 수집된 경고는 1건이었다. 경고를 숨기는 것으로 구조가 복구되지는 않는다.
같은 입력에 필요한 깊이만 지정한다
이 예제는 최상위 객체를 깊이 0으로 보면 service가 1, policy가 2, retry가 3에 있다. 같은 입력의 기본값과 명시적인 -Depth 2는 같은 JSON을 냈다. 다음 코드는 입력을 그대로 두고 직렬화 깊이만 3으로 바꾼 뒤, retry가 객체인지와 말단 값이 남았는지를 확인한다.
$json = ConvertTo-Json -InputObject $payload -Depth 3 -Compress `
-WarningVariable jsonWarnings -WarningAction Continue
$copy = ConvertFrom-Json -InputObject $json
$json
$jsonWarnings.Count
$copy.service.policy.retry.GetType().FullName
$copy.service.policy.retry.limit
이번에는 경고가 나오지 않았고 출력은 다음과 같았다.
{"service":{"policy":{"retry":{"limit":3}}}}
0
System.Management.Automation.PSCustomObject
3
retry의 값이 JSON 객체로 남았고, 다시 읽은 뒤에도 limit 값 3을 찾을 수 있었다. 두 경우 모두 JSON 파싱은 성공했지만, 필요한 구조를 유지한 쪽은 -Depth 3을 지정한 결과다. -Compress는 두 비교에 모두 사용했으며, 공식 문서에서 설명하는 역할도 출력 공백을 줄이는 것이다.
자기 코드에서는 $payload를 JSON으로 바꾸려는 실제 객체로, service.policy.retry와 limit를 보존해야 할 속성 경로로 바꿔 확인한다. 타입을 조회할 속성이 없거나 $null이면 그 앞 단계부터 확인한다. 이미 문자열로 바뀐 JSON을 다시 읽는 단계에 옵션을 붙여서는 사라진 객체를 되살릴 수 없다. 원래 객체를 직렬화하는 ConvertTo-Json의 깊이를 수정하고 JSON을 새로 만들어야 한다.
-Depth의 허용 범위는 0부터 100까지지만, 이 입력에 최대값은 필요하지 않았다. 보존할 속성 경로에 맞는 깊이를 정하고, 경고가 사라졌는지와 그 경로의 타입·값이 기대와 맞는지를 함께 확인한다. 구조가 달라지면 이 확인도 다시 해야 한다.
자동화에서 깊이 초과 경고를 무시한 채 결과를 쓰고 싶지 않다면, 변환하는 명령에 -WarningAction Stop을 지정할 수 있다.
$json = ConvertTo-Json -InputObject $payload -Depth 3 -Compress `
-WarningAction Stop
위 입력에서는 정상적으로 JSON을 반환했다. 같은 명령에서 -Depth 3을 빼면 기본 깊이의 경고 때문에 ActionPreferenceStopException으로 중단됐다. 이 옵션은 부족한 깊이를 늘려 주는 기능이 아니라, 경고가 있는 변환을 중단하는 조건이다.
깊이 초과 경고는 공식 문서의 설명 (새 탭에서 열림)상 PowerShell 7.1부터 제공된다. 이번 실행은 7.6.6의 순환참조 없는 PSCustomObject와 정수 말단을 확인한 것으로, 다른 버전·운영체제·순환참조의 동작은 검증하지 않았다. 경고 유무만으로 모든 데이터의 보존을 보장할 수는 없으므로, 실제로 필요한 중첩 속성을 다시 읽어 확인한다.