Null value
Closed this issue · 9 comments
when I want to create a shapefile and insert a null value in it, I get a system object not supported error.
Fine, but the value is an int?
please post some code and/or sample data to explain the procedure you use
This is the example given with a type modifier
` var features = new List();
for (var i = 1; i < 5; i++)
{
var lineCoords = new List
{
new(i, i + 1, i),
new(i, i, i),
new(i + 1, i, i)
};
var line = new LineString(lineCoords.ToArray());
var mline = new MultiLineString(new LineString[] { line });
int? test = null;
var attributes = new AttributesTable
{
{ "date", new DateTime() },
{ "float", i * 0.1 },
{ "int", test },
{ "logical", i % 2 == 0 },
{ "text", i.ToString("0.00") }
};
var feature = new Feature(mline, attributes);
features.Add(feature);
}
Shapefile.WriteAllFeatures(features, "t");`
and i got that
System.NotSupportedException: Unsupported dBASE field type: RuntimeType (System.Object).
System.NotSupportedException
Unsupported dBASE field type: RuntimeType (System.Object)
at NetTopologySuite.IO.Esri.Dbf.Fields.DbfField.Create(String name, Type type)
at NetTopologySuite.IO.Esri.FeatureExtensions.GetDbfFields(IAttributesTable attributes)
at NetTopologySuite.IO.Esri.Shapefile.WriteAllFeatures(IEnumerable`1 features, String shpPath, String projection, Encoding encoding)
When I look in debug mode I see that this is the field I modified to make it nullable
I experienced similar problem.
For example it's not possible to write nullable integer. I tried using null and DBNull.Value, but nothing worked.
same with all data type
We can read null value but we can't write null value
New problem when a date is null its value is '00000000' or when reading it causes this
The DateTime represented by the string '00000000' is not supported in calendar
Thanks for pointing that out! We're working on a fix for this issue. For now, if there is a nullable field, you have to set all field definitions manually:
var features = new List<Feature>();
for (var i = 1; i < 5; i++)
{
var lineCoords = new List<Coordinate>
{
new(i, i + 1),
new(i, i),
new(i + 1, i)
};
var line = new LineString(lineCoords.ToArray());
var mline = new MultiLineString(new LineString[] { line });
int? test = null;
var attributes = new AttributesTable
{
{ "date", new DateTime() },
{ "float", i * 0.1 },
{ "int", test },
{ "logical", i % 2 == 0 },
{ "text", i.ToString("0.00") }
};
var feature = new Feature(mline, attributes);
features.Add(feature);
}
var fields = new List<DbfField>();
fields.AddDateField("date");
fields.AddFloatField("float");
fields.AddNumericInt32Field("int");
fields.AddLogicalField("logical");
fields.AddCharacterField("text");
var options = new ShapefileWriterOptions(ShapeType.PolyLine, fields.ToArray());
var shpPath = TestShapefiles.GetTempShpPath();
using (var shpWriter = Shapefile.OpenWrite(shpPath, options))
{
shpWriter.Write(features);
}
TestShapefiles.DeleteShp(shpPath);
Another solution is to use pure SHP Writer without the AttributesTable
bridge:
var fields = new List<DbfField>();
var dateField = fields.AddDateField("date");
var floatField = fields.AddFloatField("float");
var intField = fields.AddNumericInt32Field("int");
var logicalField = fields.AddLogicalField("logical");
var textField = fields.AddCharacterField("text");
var options = new ShapefileWriterOptions(ShapeType.PolyLine, fields.ToArray());
var shpPath = TestShapefiles.GetTempShpPath();
using (var shpWriter = Shapefile.OpenWrite(shpPath, options))
{
for (var i = 1; i < 5; i++)
{
var lineCoords = new List<Coordinate>
{
new(i, i + 1),
new(i, i),
new(i + 1, i)
};
var line = new LineString(lineCoords.ToArray());
var mline = new MultiLineString(new LineString[] { line });
int? test = null;
shpWriter.Geometry = mline;
dateField.DateValue = DateTime.Now;
floatField.NumericValue = i * 0.1;
intField.NumericValue = test;
logicalField.LogicalValue = i % 2 == 0;
textField.StringValue = i.ToString("0.00");
shpWriter.Write();
}
}
TestShapefiles.DeleteShp(shpPath);
This will be more performant as in this case there is no boxing/unboxing.
I had a similar problem with datetime attributes. The problem was not that I couldn't write null values, it was caused by having all values of a particular attribute set as null. If the first value was not null it worked.
Anyway, I just wanted to say to @KubaSzostak that the latest exemple you posted is working really good. You should place it in the main github page. This also solved a problem I had with writing a shapefile containing only points where the coordinates of the points were not registering properly (ArcGIS said the shape file had no coordonate system assigned)
When all features have a null value, the field type cannot be detected correctly. To solve this, the AttributesTable
needs to be extended to store attribute types along with attribute values, so that AttributesTable.GetType()
returns property attribute type instead of default typeof(object)
.
Below is a sample code that demonstrates how to receive attribute type from CLR type.
private static void TestClrTypes()
{
int? i0 = null;
int? i1 = 1;
int i2 = 2;
string s0 = null;
string s1 = "s1";
WriteType("int? i0 = null ", i0);
WriteType("int? i1 = 1 ", i1);
WriteType("int i2 = 2 ", i2);
WriteType("string s0 = null", s0);
WriteType("string s1 = \"s1\"", s1);
}
private static void WriteType<T>(string name, T value)
{
var valueText = value?.ToString() ?? "<null>";
var valueType = typeof(T);
var underlyingType = Nullable.GetUnderlyingType(valueType) ?? valueType;
Console.WriteLine($"{name}");
Console.WriteLine($"- Value: {valueText}");
Console.WriteLine($"- ValueType: {valueType}");
Console.WriteLine($"- UnderlyingType: {underlyingType}");
Console.WriteLine();
}
int? i0 = null
- Value: <null>
- ValueType: System.Nullable`1[System.Int32]
- UnderlyingType: System.Int32
int? i1 = 1
- Value: 1
- ValueType: System.Nullable`1[System.Int32]
- UnderlyingType: System.Int32
int i2 = 2
- Value: 2
- ValueType: System.Int32
- UnderlyingType: System.Int32
string s0 = null
- Value: <null>
- ValueType: System.String
- UnderlyingType: System.String
string s1 = "s1"
- Value: s1
- ValueType: System.String
- UnderlyingType: System.String