From a54ee5792122efd66295d7382f29c2973d07d873 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Thu, 29 Oct 2026 21:31:24 -0700 Subject: [PATCH 1/2] Trim the S-100 backslashes from the subfield label before a repeating group S-100 Part 10a data descriptive records put "\\" just before the '*' that starts a repeating group, as in the S-101 2.0 DSID descriptor "...!DSAB!DSED\\*DSTC". The reader kept the backslashes in the name of the subfield before the '*', so DSED, IUIN, VCID and FAUI were named "DSED\\" and so on, and lookups by name missed them. Trailing backslashes are now trimmed from every subfield label, in both the labels-section and descriptor parsing paths. Reader tests cover the S-101 2.0 DSID and C3IL descriptors. No public API changes. Fixes #38 --- .../Iso8211DataDescriptiveRecordReader.cs | 10 +++- ...Iso8211DataDescriptiveRecordReaderTests.cs | 49 +++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs b/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs index 0481042..f020aca 100644 --- a/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs +++ b/src/EncDotNet.Iso8211/Iso8211DataDescriptiveRecordReader.cs @@ -366,7 +366,8 @@ private static void ParseSubfieldLabels( int starIndex = rawName.IndexOf('*'); if (starIndex > 0) { - string leaderName = rawName.Substring(0, starIndex); + // S-100 Part 10a writes "\\" just before the '*'; it is not part of the label. + string leaderName = TrimLabel(rawName.Substring(0, starIndex)); if (!string.IsNullOrEmpty(leaderName)) { names.Add(leaderName); @@ -378,6 +379,7 @@ private static void ParseSubfieldLabels( } } + rawName = TrimLabel(rawName); if (!string.IsNullOrEmpty(rawName)) { names.Add(rawName); @@ -388,6 +390,11 @@ private static void ParseSubfieldLabels( subfieldNames = names.ToImmutable(); } + /// + /// Removes the trailing backslashes S-100 Part 10a places before a repeating-group *. + /// + private static string TrimLabel(string label) => label.TrimEnd('\\'); + /// /// Parses the descriptor portion of a DDR field into a field name and subfield names. /// @@ -439,6 +446,7 @@ private static void ParseDescriptors( rawName = rawName.Substring(1); } + rawName = TrimLabel(rawName); if (!string.IsNullOrEmpty(rawName)) { names.Add(rawName); diff --git a/tests/EncDotNet.UnitTests/Iso8211DataDescriptiveRecordReaderTests.cs b/tests/EncDotNet.UnitTests/Iso8211DataDescriptiveRecordReaderTests.cs index 20d26b0..d34734b 100644 --- a/tests/EncDotNet.UnitTests/Iso8211DataDescriptiveRecordReaderTests.cs +++ b/tests/EncDotNet.UnitTests/Iso8211DataDescriptiveRecordReaderTests.cs @@ -1528,6 +1528,55 @@ public void Parse_TwoUtFormat_ConcatenatedArrayWithLeaderAndRepeating_ParsesCorr Assert.True(c3il.SubfieldDefinitions[3].IsRepeating); } + [Fact] + public void Parse_TwoUtFormat_S100BackslashesBeforeStar_AreTrimmedFromLeaderName() + { + // Arrange — S-100 Part 10a writes "\\" before the '*' that starts the repeating group + // (S-101 2.0 DSID descriptor). + var record = CreateDdrRecordTwoUt( + tag: "DSID", + dataStructureCode: '1', + dataTypeCode: '6', + fieldName: "Dataset Identification", + subfieldLabels: @"RCNM!RCID!ENSP!ENED!PRSP!PRED!PROF!DSNM!DSTL!DSRD!DSLG!DSAB!DSED\\*DSTC", + formatControls: "(b11,b14,11A,b11)"); + + // Act + var ddr = Iso8211DataDescriptiveRecordReader.Read(record); + var dsid = ddr.FieldDefinitions[0]; + + // Assert + Assert.Equal(14, dsid.SubfieldDefinitions.Count); + Assert.Equal("DSED", dsid.SubfieldDefinitions[12].Name); + Assert.Equal("DSTC", dsid.SubfieldDefinitions[13].Name); + Assert.Equal(13, dsid.RepeatingSubfieldStartIndex); + Assert.False(dsid.SubfieldDefinitions[12].IsRepeating); + Assert.True(dsid.SubfieldDefinitions[13].IsRepeating); + } + + [Fact] + public void Parse_TwoUtFormat_S100BackslashesBeforeStar_SingleLeaderSubfield() + { + // Arrange — S-101 2.0 C3IL descriptor + var record = CreateDdrRecordTwoUt( + tag: "C3IL", + dataStructureCode: '3', + dataTypeCode: '5', + fieldName: "3-D Integer Coordinate List", + subfieldLabels: @"VCID\\*YCOO!XCOO!ZCOO", + formatControls: "(b11,3b24)"); + + // Act + var ddr = Iso8211DataDescriptiveRecordReader.Read(record); + var c3il = ddr.FieldDefinitions[0]; + + // Assert + Assert.Equal(4, c3il.SubfieldDefinitions.Count); + Assert.Equal("VCID", c3il.SubfieldDefinitions[0].Name); + Assert.Equal("YCOO", c3il.SubfieldDefinitions[1].Name); + Assert.Equal(1, c3il.RepeatingSubfieldStartIndex); + } + [Fact] public void Parse_TwoUtFormat_VectorStyleStarAtStart_StillWorks() { From a523dda87daf851c68f7cb51a5e4498c0c071a77 Mon Sep 17 00:00:00 2001 From: Phillip Hoff Date: Thu, 29 Oct 2026 21:35:41 -0700 Subject: [PATCH 2/2] Update MessagePack to 3.1.9 3.1.8 has a moderate advisory (GHSA-qhrr-8q5h-9q3h, nested arrays can exceed buffer length limits), and restore treats the NU1902 warning as an error, so every CI build failed. --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index fb1dcee..f0ba2a3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,7 +13,7 @@ - +