using System.Globalization; namespace Infrastructure.StockData; /// /// SEIBro getStkStatInfo(주식 종목 정보) 응답 파서 — 순수 C#. "SEIBro 종목 보강" (Wave 0, Stock.EnrichFromSeibro 입력). /// 행 필드(사용분): ISSUCO_CUSTNO / ISIN / SHOTN_ISIN / PVAL(액면가) / VOTRNO(1주당 의결권수) / /// TOTAL_STK_CNT(총발행주식수) / ELTSC_YN([Y]전자 [N]예탁 → bool, 그 외 null). /// 숫자 필드는 생략/빈 값/비숫자 시 null (0 과 구분). TOTAL_STK_CNT 는 스펙상 NUMBER(15,4) 라 decimal 로 읽어 long 절사. /// public static class SeibroStkStatParser { /// 주식 종목 정보 1행 — 전 필드 nullable (SEIBro 는 필드 생략 가능) public sealed record Row( int? IssucoCustno, string? Isin, string? ShortCode, decimal? FaceValue, decimal? VotingRights, long? TotalIssuedShares, bool? IsElectronicSecurity ); public static IReadOnlyList Parse(SeibroXml.Envelope envelope) { var rows = new List(); foreach (var fields in envelope.Rows) { rows.Add(new Row( ParseNullableInt(SeibroXml.GetOrNull(fields, "ISSUCO_CUSTNO")), SeibroXml.GetOrNull(fields, "ISIN")?.Trim(), SeibroXml.GetOrNull(fields, "SHOTN_ISIN")?.Trim(), ParseNullableDecimal(SeibroXml.GetOrNull(fields, "PVAL")), ParseNullableDecimal(SeibroXml.GetOrNull(fields, "VOTRNO")), ParseNullableLong(SeibroXml.GetOrNull(fields, "TOTAL_STK_CNT")), ParseYn(SeibroXml.GetOrNull(fields, "ELTSC_YN")) )); } return rows; } private static int? ParseNullableInt(string? raw) { return int.TryParse(raw, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) ? value : null; } private static decimal? ParseNullableDecimal(string? raw) { return decimal.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var value) ? value : null; } /// 총발행주식수 — 스펙 NUMBER(15,4) 대비 decimal 로 읽고 정수 절사 private static long? ParseNullableLong(string? raw) { return decimal.TryParse(raw, NumberStyles.Float, CultureInfo.InvariantCulture, out var value) ? (long)value : null; } private static bool? ParseYn(string? raw) { return raw?.Trim().ToUpperInvariant() switch { "Y" => true, "N" => false, _ => null }; } }