diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs index bdb6ebd..c444268 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/DbfStreamExtensions.cs @@ -115,7 +115,7 @@ public static DbfField ReadDbaseFieldDescriptor(this Stream stream, Encoding enc } else if (type == DbfType.Date) { - return new DbfDateField(name); + return new DbfDateField(name, length); } else if (type == DbfType.Numeric) { @@ -127,7 +127,7 @@ public static DbfField ReadDbaseFieldDescriptor(this Stream stream, Encoding enc } else if (type == DbfType.Logical) { - return new DbfLogicalField(name); + return new DbfLogicalField(name, length); } else { diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs index 3f4dd03..9e6aacf 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfDateField.cs @@ -11,15 +11,16 @@ namespace NetTopologySuite.IO.Esri.Dbf.Fields /// public class DbfDateField : DbfField { - private static readonly int FieldLength = 8; // This width is fixed and cannot be changed + private const int FieldLength = 8; // This width is fixed and cannot be changed private static readonly string DateFormat = "yyyyMMdd"; /// /// Initializes a new instance of the field class. /// /// Field name. - public DbfDateField(string name) - : base(name, DbfType.Date, FieldLength, 0) + /// Field length. + public DbfDateField(string name, int length = FieldLength) + : base(name, DbfType.Date, length, 0) { } diff --git a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs index 36c9610..5e5f752 100644 --- a/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs +++ b/src/NetTopologySuite.IO.Esri.Shapefile/Dbf/Fields/DbfLogicalField.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Linq; +using System.Text; namespace NetTopologySuite.IO.Esri.Dbf.Fields { @@ -12,7 +13,7 @@ public class DbfLogicalField : DbfField private readonly static byte DefaultValue = (byte)' '; // (byte)'?'; Initialized to 0x20 (space) otherwise T or F (http://www.dbase.com/KnowledgeBase/int/db7_file_fmt.htm) private readonly static byte TrueValue = (byte)'T'; private readonly static byte FalseValue = (byte)'F'; - private readonly static int FieldLength = 1; // This width is fixed and cannot be changed + private const int FieldLength = 1; // This width is fixed and cannot be changed private readonly static string TrueValues = "TtYy"; private readonly static string FalseValues = "FfNn"; @@ -22,8 +23,9 @@ public class DbfLogicalField : DbfField /// Initializes a new instance of the field class. /// /// Field name. - public DbfLogicalField(string name) - : base(name, DbfType.Logical, FieldLength, 0) + /// Field length. + public DbfLogicalField(string name, int length = FieldLength) + : base(name, DbfType.Logical, length, 0) { } @@ -44,13 +46,22 @@ public override object Value internal override void ReadValue(Stream stream) { - var logicalValue = stream.ReadByteChar(); + // Logic column should have a Length of 1. However, some data providers produce shapfiles with a length greater than 1. + // Handle also those not specification compliant cases. + var logicalValueString = stream.ReadString(Length, Encoding.ASCII)?.Trim(); + if (string.IsNullOrEmpty(logicalValueString)) + { + LogicalValue = null; + return; + } + + var logicalValueChar = logicalValueString[0]; - if (TrueValues.Contains(logicalValue)) + if (TrueValues.Contains(logicalValueChar)) { LogicalValue = true; } - else if (FalseValues.Contains(logicalValue)) + else if (FalseValues.Contains(logicalValueChar)) { LogicalValue = false; } @@ -78,7 +89,4 @@ internal override void WriteValue(Stream stream) } } - - - } diff --git a/test/NetTopologySuite.IO.Esri.Test/Issues/Issue051.cs b/test/NetTopologySuite.IO.Esri.Test/Issues/Issue051.cs new file mode 100644 index 0000000..25da8ab --- /dev/null +++ b/test/NetTopologySuite.IO.Esri.Test/Issues/Issue051.cs @@ -0,0 +1,46 @@ +using System.Collections.Generic; +using System.Linq; +using NetTopologySuite.Features; +using NetTopologySuite.IO.Esri.Dbf; +using NUnit.Framework; + +namespace NetTopologySuite.IO.Esri.Test.Issues +{ + /// + /// https://github.com/NetTopologySuite/NetTopologySuite.IO.Esri/issues/51 + /// + internal class Issue051 + { + [Test] + public void Read_When_ColumnLengthMissmatch() + { + var shpPath = TestShapefiles.PathTo("#51-columnLengthMissmatch.dbf"); + List attributes = null; + Assert.DoesNotThrow(() => + { + var dbfReader = new DbfReader(shpPath); + attributes = dbfReader.ToList(); + }); + + Assert.NotNull(attributes); + + //First row is null and empty + Assert.AreEqual(string.Empty, attributes[0]["RoadLinkId"]); + Assert.AreEqual(null, attributes[0]["Tunnel"]); + Assert.AreEqual(null, attributes[0]["Bridge"]); + Assert.AreEqual(null, attributes[0]["CarAccess"]); + Assert.AreEqual(null, attributes[0]["Walk"]); + Assert.AreEqual(null, attributes[0]["MF_4_7"]); + Assert.AreEqual(null, attributes[0]["COREID"]); + + //Second row has value but length is missmatch in each column + Assert.AreEqual("osgb4000000080908489", attributes[1]["RoadLinkId"]); + Assert.AreEqual(true, attributes[1]["Tunnel"]); + Assert.AreEqual(false, attributes[1]["Bridge"]); + Assert.AreEqual(true, attributes[1]["CarAccess"]); + Assert.AreEqual(4.8, attributes[1]["Walk"]); + Assert.AreEqual(50, attributes[1]["MF_4_7"]); + Assert.AreEqual(55483, attributes[1]["COREID"]); + } + } +} diff --git a/test/TestData/TestShapefiles/#51-columnLengthMissmatch.dbf b/test/TestData/TestShapefiles/#51-columnLengthMissmatch.dbf new file mode 100644 index 0000000..46e8086 Binary files /dev/null and b/test/TestData/TestShapefiles/#51-columnLengthMissmatch.dbf differ