diff --git a/MatFileHandler.Tests/MatFileReaderTests.cs b/MatFileHandler.Tests/MatFileReaderTests.cs
index 88fd0e8..b317cf3 100755
--- a/MatFileHandler.Tests/MatFileReaderTests.cs
+++ b/MatFileHandler.Tests/MatFileReaderTests.cs
@@ -1,5 +1,6 @@
// Copyright 2017-2018 Alexander Luzgarev
+using System;
using System.IO;
using System.Linq;
using System.Numerics;
@@ -388,6 +389,66 @@ namespace MatFileHandler.Tests
Assert.That(ppp["y"].ConvertToDoubleArray(), Is.EquivalentTo(new[] { 200.0 }));
}
+ ///
+ /// Test datetime objects.
+ ///
+ [Test]
+ public void TestDatetime()
+ {
+ var matFile = GetTests("good")["datetime"];
+ var d = matFile["d"].Value as IMatObject;
+ var datetime = new DatetimeAdapter(d);
+ Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 2 }));
+ Assert.That(datetime[0], Is.EqualTo(new DateTimeOffset(2000, 1, 1, 0, 0, 0, TimeSpan.Zero)));
+ Assert.That(datetime[1], Is.EqualTo(new DateTimeOffset(1987, 1, 2, 3, 4, 5, TimeSpan.Zero)));
+ }
+
+ ///
+ /// Another test for datetime objects.
+ ///
+ [Test]
+ public void TestDatetime2()
+ {
+ var matFile = GetTests("good")["datetime2"];
+ var d = matFile["d"].Value as IMatObject;
+ var datetime = new DatetimeAdapter(d);
+ Assert.That(datetime.Dimensions, Is.EquivalentTo(new[] { 1, 1 }));
+ var diff = new DateTimeOffset(2, 1, 1, 1, 1, 1, 235, TimeSpan.Zero);
+ Assert.That(datetime[0] - diff < TimeSpan.FromMilliseconds(1));
+ Assert.That(diff - datetime[0] < TimeSpan.FromMilliseconds(1));
+ }
+
+ ///
+ /// Test string objects.
+ ///
+ [Test]
+ public void TestString()
+ {
+ var matFile = GetTests("good")["string"];
+ var s = matFile["s"].Value as IMatObject;
+ var str = new StringAdapter(s);
+ Assert.That(str.Dimensions, Is.EquivalentTo(new[] { 4, 1 }));
+ Assert.That(str[0], Is.EqualTo("abc"));
+ Assert.That(str[1], Is.EqualTo("defgh"));
+ Assert.That(str[2], Is.EqualTo("абвгд"));
+ Assert.That(str[3], Is.EqualTo("æøå"));
+ }
+
+ ///
+ /// Test duration objects.
+ ///
+ [Test]
+ public void TestDuration()
+ {
+ var matFile = GetTests("good")["duration"];
+ var d = matFile["d"].Value as IMatObject;
+ var duration = new DurationAdapter(d);
+ Assert.That(duration.Dimensions, Is.EquivalentTo(new[] { 1, 3 }));
+ Assert.That(duration[0], Is.EqualTo(TimeSpan.FromTicks(12345678L)));
+ Assert.That(duration[1], Is.EqualTo(new TimeSpan(0, 2, 4)));
+ Assert.That(duration[2], Is.EqualTo(new TimeSpan(1, 3, 5)));
+ }
+
private static AbstractTestDataFactory GetTests(string factoryName) =>
new MatTestDataFactory(Path.Combine(TestDirectory, factoryName));
diff --git a/MatFileHandler.Tests/test-data/good/datetime.mat b/MatFileHandler.Tests/test-data/good/datetime.mat
new file mode 100644
index 0000000..6f6899f
Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/datetime.mat differ
diff --git a/MatFileHandler.Tests/test-data/good/datetime2.mat b/MatFileHandler.Tests/test-data/good/datetime2.mat
new file mode 100644
index 0000000..d9d19c5
Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/datetime2.mat differ
diff --git a/MatFileHandler.Tests/test-data/good/duration.mat b/MatFileHandler.Tests/test-data/good/duration.mat
new file mode 100644
index 0000000..162bbe5
Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/duration.mat differ
diff --git a/MatFileHandler.Tests/test-data/good/string.mat b/MatFileHandler.Tests/test-data/good/string.mat
new file mode 100644
index 0000000..b86a9c9
Binary files /dev/null and b/MatFileHandler.Tests/test-data/good/string.mat differ
diff --git a/MatFileHandler/DatetimeAdapter.cs b/MatFileHandler/DatetimeAdapter.cs
new file mode 100644
index 0000000..c86371f
--- /dev/null
+++ b/MatFileHandler/DatetimeAdapter.cs
@@ -0,0 +1,62 @@
+// Copyright 2017-2018 Alexander Luzgarev
+
+using System;
+using System.Linq;
+using System.Numerics;
+
+namespace MatFileHandler
+{
+ ///
+ /// A better interface for using datetime objects.
+ ///
+ public class DatetimeAdapter
+ {
+ private readonly double[] data;
+ private readonly double[] data2;
+ private readonly int[] dimensions;
+
+ private readonly DateTimeOffset epoch;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Source datetime object.
+ public DatetimeAdapter(IArray array)
+ {
+ var matObject = array as IMatObject;
+ if (matObject?.ClassName != "datetime")
+ {
+ throw new ArgumentException("The object provided is not a datetime.");
+ }
+
+ epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
+ var dataArray = matObject["data", 0] as IArrayOf;
+ if (dataArray is null)
+ {
+ var dataComplex = matObject["data", 0] as IArrayOf;
+ var complexData = dataComplex.ConvertToComplexArray();
+ data = complexData.Select(c => c.Real).ToArray();
+ data2 = complexData.Select(c => c.Imaginary).ToArray();
+ dimensions = dataComplex.Dimensions;
+ }
+ else
+ {
+ data = dataArray.ConvertToDoubleArray();
+ data2 = new double[data.Length];
+ dimensions = dataArray.Dimensions;
+ }
+ }
+
+ ///
+ /// Gets datetime array dimensions.
+ ///
+ public int[] Dimensions => dimensions;
+
+ ///
+ /// Gets values of datetime object at given position in the array converted to .
+ ///
+ /// Indices.
+ /// Value converted to .
+ public DateTimeOffset this[params int[] list] => epoch.AddMilliseconds(data[Dimensions.DimFlatten(list)]);
+ }
+}
\ No newline at end of file
diff --git a/MatFileHandler/DurationAdapter.cs b/MatFileHandler/DurationAdapter.cs
new file mode 100644
index 0000000..c7a6a35
--- /dev/null
+++ b/MatFileHandler/DurationAdapter.cs
@@ -0,0 +1,44 @@
+// Copyright 2017-2018 Alexander Luzgarev
+
+using System;
+
+namespace MatFileHandler
+{
+ ///
+ /// A better interface for using duration objects.
+ ///
+ public class DurationAdapter
+ {
+ private readonly int[] dimensions;
+ private readonly double[] data;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Source duration object.
+ public DurationAdapter(IArray array)
+ {
+ var matObject = array as IMatObject;
+ if (matObject?.ClassName != "duration")
+ {
+ throw new ArgumentException("The object provided is not a duration.");
+ }
+
+ var dataObject = matObject["millis", 0];
+ data = dataObject.ConvertToDoubleArray();
+ dimensions = dataObject.Dimensions;
+ }
+
+ ///
+ /// Gets duration array dimensions.
+ ///
+ public int[] Dimensions => dimensions;
+
+ ///
+ /// Gets duration object at given position.
+ ///
+ /// Indices.
+ /// Value.
+ public TimeSpan this[params int[] list] => TimeSpan.FromTicks((long)(10000.0 * data[Dimensions.DimFlatten(list)]));
+ }
+}
\ No newline at end of file
diff --git a/MatFileHandler/MatFileHandler.csproj b/MatFileHandler/MatFileHandler.csproj
index ebd7993..7152830 100755
--- a/MatFileHandler/MatFileHandler.csproj
+++ b/MatFileHandler/MatFileHandler.csproj
@@ -1,7 +1,7 @@
netstandard2.0;net461
- 1.3.0-beta2
+ 1.3.0-beta3
MatFileHandler
A library for reading and writing MATLAB .mat files.
Alexander Luzgarev
diff --git a/MatFileHandler/OpaqueLink.cs b/MatFileHandler/OpaqueLink.cs
index fcb0cfe..ed71d2f 100644
--- a/MatFileHandler/OpaqueLink.cs
+++ b/MatFileHandler/OpaqueLink.cs
@@ -107,8 +107,8 @@ namespace MatFileHandler
return false;
}
- var objectPosition = IndexToObjectId[index];
- var objectInfo = subsystemData.ObjectInformation.First(pair => pair.Value.Position == objectPosition).Value;
+ var objectId = IndexToObjectId[index];
+ var objectInfo = subsystemData.ObjectInformation[objectId];
var fieldId = objectInfo.FieldLinks[fieldIndex];
output = subsystemData.Data[fieldId];
return true;
diff --git a/MatFileHandler/StringAdapter.cs b/MatFileHandler/StringAdapter.cs
new file mode 100644
index 0000000..afc5f80
--- /dev/null
+++ b/MatFileHandler/StringAdapter.cs
@@ -0,0 +1,78 @@
+// Copyright 2017-2018 Alexander Luzgarev
+
+using System;
+using System.Text;
+
+namespace MatFileHandler
+{
+ ///
+ /// A better interface for using string objects.
+ ///
+ public class StringAdapter
+ {
+ private readonly int[] dimensions;
+ private readonly string[] strings;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Source string object.
+ public StringAdapter(IArray array)
+ {
+ var matObject = array as IMatObject;
+ if (matObject?.ClassName != "string")
+ {
+ throw new ArgumentException("The object provided is not a string.");
+ }
+
+ var binaryData = matObject["any", 0] as IArrayOf;
+
+ (dimensions, strings) = ParseBinaryData(binaryData.Data);
+ }
+
+ ///
+ /// Gets string array dimensions.
+ ///
+ public int[] Dimensions => dimensions;
+
+ ///
+ /// Gets string object at given position.
+ ///
+ /// Indices.
+ /// Value.
+ public string this[params int[] list] => strings[Dimensions.DimFlatten(list)];
+
+ private static (int[] dimensions, string[] strings) ParseBinaryData(ulong[] binaryData)
+ {
+ var numberOfDimensions = (int)binaryData[1];
+ var d = new int[numberOfDimensions];
+ for (var i = 0; i < numberOfDimensions; i++)
+ {
+ d[i] = (int)binaryData[i + 2];
+ }
+
+ var numberOfElements = d.NumberOfElements();
+ var start = numberOfDimensions + 2;
+ var lengths = new int[numberOfElements];
+ for (var i = 0; i < numberOfElements; i++)
+ {
+ lengths[i] = (int)binaryData[start + i];
+ }
+
+ var strings = new string[numberOfElements];
+
+ start += numberOfElements;
+ var numberOfUlongsLeft = binaryData.Length - start;
+ var bytes = new byte[numberOfUlongsLeft * sizeof(ulong)];
+ Buffer.BlockCopy(binaryData, start * sizeof(ulong), bytes, 0, bytes.Length);
+ var counter = 0;
+ for (var i = 0; i < numberOfElements; i++)
+ {
+ strings[i] = Encoding.Unicode.GetString(bytes, counter * 2, lengths[i] * 2);
+ counter += lengths[i];
+ }
+
+ return (d, strings);
+ }
+ }
+}
\ No newline at end of file
diff --git a/MatFileHandler/SubsystemData.cs b/MatFileHandler/SubsystemData.cs
index ab65292..fd987b8 100644
--- a/MatFileHandler/SubsystemData.cs
+++ b/MatFileHandler/SubsystemData.cs
@@ -125,11 +125,9 @@ namespace MatFileHandler
///
/// Initializes a new instance of the class.
///
- /// Position of object in the object information table.
/// A dictionary mapping the field indices to "field values" indices.
- public ObjectInfo(int position, Dictionary fieldLinks)
+ public ObjectInfo(Dictionary fieldLinks)
{
- Position = position;
this.fieldLinks = fieldLinks ?? throw new ArgumentNullException(nameof(fieldLinks));
}
@@ -137,11 +135,6 @@ namespace MatFileHandler
/// Gets mapping between the field indices and "field values" indices.
///
public IReadOnlyDictionary FieldLinks => fieldLinks;
-
- ///
- /// Gets position of object in the object information table.
- ///
- public int Position { get; }
}
}
}
\ No newline at end of file
diff --git a/MatFileHandler/SubsystemDataReader.cs b/MatFileHandler/SubsystemDataReader.cs
index ce7f18b..9b1b6fc 100644
--- a/MatFileHandler/SubsystemDataReader.cs
+++ b/MatFileHandler/SubsystemDataReader.cs
@@ -50,8 +50,19 @@ namespace MatFileHandler
}
}
+ var numberOfEmbeddedObjects = (offsets[4] - offsets[3] - 8) / 16;
+ Dictionary> embeddedObjectPositionsToValues = null;
+ using (var stream = new MemoryStream(info, offsets[3], offsets[4] - offsets[3]))
+ {
+ using (var reader = new BinaryReader(stream))
+ {
+ embeddedObjectPositionsToValues =
+ ReadEmbeddedObjectPositionsToValuesMapping(reader, numberOfEmbeddedObjects);
+ }
+ }
+
var numberOfObjects = ((offsets[5] - offsets[4]) / 24) - 1;
- Dictionary objectClasses = null;
+ Dictionary objectClasses = null;
using (var stream = new MemoryStream(info, offsets[4], offsets[5] - offsets[4]))
{
using (var reader = new BinaryReader(stream))
@@ -60,12 +71,14 @@ namespace MatFileHandler
}
}
- Dictionary> objectToFields = null;
+ var numberOfObjectPositions = objectClasses.Values.Count(x => x.ObjectPosition != 0);
+
+ Dictionary> objectPositionsToValues = null;
using (var stream = new MemoryStream(info, offsets[5], offsets[6] - offsets[5]))
{
using (var reader = new BinaryReader(stream))
{
- objectToFields = ReadObjectToFieldsMapping(reader, numberOfObjects);
+ objectPositionsToValues = ReadObjectPositionsToValuesMapping(reader, numberOfObjectPositions);
}
}
@@ -74,7 +87,8 @@ namespace MatFileHandler
classIdToName,
fieldNames,
objectClasses,
- objectToFields);
+ objectPositionsToValues,
+ embeddedObjectPositionsToValues);
var allFields = objectInformation.Values.SelectMany(obj => obj.FieldLinks.Values);
var data = new Dictionary();
@@ -82,120 +96,77 @@ namespace MatFileHandler
{
data[i] = TransformOpaqueData(opaqueData[i + 2], subsystemData);
}
+
return new SubsystemData(classInformation, objectInformation, data);
}
- private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData)
- {
- if (array is MatNumericalArrayOf uintArray)
- {
- if (uintArray.Data[0] == 3707764736u)
- {
- var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data);
- return new OpaqueLink(
- uintArray.Name,
- string.Empty,
- string.Empty,
- dimensions,
- array as DataElement,
- indexToObjectId,
- classIndex,
- subsystemData);
- }
- }
-
- return array;
- }
-
- private static Dictionary ReadObjectClasses(BinaryReader reader, int numberOfObjects)
- {
- var result = new Dictionary();
- reader.ReadBytes(24);
- for (var i = 0; i < numberOfObjects; i++)
- {
- var classId = reader.ReadInt32();
- reader.ReadBytes(12);
- var objectPosition = reader.ReadInt32();
- var objectId = reader.ReadInt32();
- result[objectPosition] = (objectId, classId);
- }
-
- return result;
- }
-
- private static (Dictionary, Dictionary) GatherClassAndObjectInformation(
- Dictionary classIdToName,
- string[] fieldNames,
- Dictionary objectClasses,
- Dictionary> objectToFields)
+ private static (Dictionary, Dictionary)
+ GatherClassAndObjectInformation(
+ Dictionary classIdToName,
+ string[] fieldNames,
+ Dictionary objectClasses,
+ Dictionary> objectPositionsToValues,
+ Dictionary> embeddedObjectPositionsToValues)
{
var classInfos = new Dictionary();
foreach (var classId in classIdToName.Keys)
{
var className = classIdToName[classId];
var fieldIds = new SortedSet();
- foreach (var objectPosition in objectToFields.Keys)
+ foreach (var objectPosition in objectPositionsToValues.Keys)
{
- var (_, thisObjectClassId) = objectClasses[objectPosition];
- if (thisObjectClassId != classId)
+ var keyValuePair = objectClasses.First(pair => pair.Value.ObjectPosition == objectPosition);
+ if (keyValuePair.Value.ClassId != classId)
{
continue;
}
- foreach (var fieldId in objectToFields[objectPosition].Keys)
+ foreach (var fieldId in objectPositionsToValues[objectPosition].Keys)
{
fieldIds.Add(fieldId);
}
}
+
+ foreach (var objectPosition in embeddedObjectPositionsToValues.Keys)
+ {
+ var keyValuePair = objectClasses.First(pair => pair.Value.EmbeddedObjectPosition == objectPosition);
+ if (keyValuePair.Value.ClassId != classId)
+ {
+ continue;
+ }
+
+ foreach (var fieldId in embeddedObjectPositionsToValues[objectPosition].Keys)
+ {
+ fieldIds.Add(fieldId);
+ }
+ }
+
var fieldToIndex = new Dictionary();
foreach (var fieldId in fieldIds)
{
fieldToIndex[fieldNames[fieldId - 1]] = fieldId;
}
+
classInfos[classId] = new SubsystemData.ClassInfo(className, fieldToIndex);
}
var objectInfos = new Dictionary();
- foreach (var objectPosition in objectToFields.Keys)
+ foreach (var objectPosition in objectPositionsToValues.Keys)
{
- var (objectId, _) = objectClasses[objectPosition];
- objectInfos[objectId] = new SubsystemData.ObjectInfo(objectPosition, objectToFields[objectPosition]);
+ var keyValuePair = objectClasses.First(pair => pair.Value.ObjectPosition == objectPosition);
+ objectInfos[keyValuePair.Key] = new SubsystemData.ObjectInfo(objectPositionsToValues[objectPosition]);
+ }
+
+ foreach (var objectPosition in embeddedObjectPositionsToValues.Keys)
+ {
+ var keyValuePair = objectClasses.First(pair => pair.Value.EmbeddedObjectPosition == objectPosition);
+ objectInfos[keyValuePair.Key] =
+ new SubsystemData.ObjectInfo(embeddedObjectPositionsToValues[objectPosition]);
}
return (classInfos, objectInfos);
}
- private static Dictionary ReadFieldToFieldDataMapping(BinaryReader reader)
- {
- var length = reader.ReadInt32();
- var result = new Dictionary();
- for (var i = 0; i < length; i++)
- {
- var x = reader.ReadInt32();
- var y = reader.ReadInt32();
- var index = x * y;
- var link = reader.ReadInt32();
- result[index] = link;
- }
- return result;
- }
-
- private static Dictionary> ReadObjectToFieldsMapping(BinaryReader reader, int numberOfObjects)
- {
- var result = new Dictionary>();
- reader.ReadBytes(8);
- for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++)
- {
- result[objectPosition] = ReadFieldToFieldDataMapping(reader);
- var position = reader.BaseStream.Position;
- if (position % 8 != 0)
- {
- reader.ReadBytes(8 - (int)(position % 8));
- }
- }
- return result;
- }
-
private static Dictionary ReadClassNames(
BinaryReader reader,
string[] fieldNames,
@@ -219,6 +190,98 @@ namespace MatFileHandler
return result;
}
+ private static Dictionary> ReadEmbeddedObjectPositionsToValuesMapping(
+ BinaryReader reader, int numberOfObjects)
+ {
+ var result = new Dictionary>();
+ reader.ReadBytes(8);
+ for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++)
+ {
+ var a = reader.ReadInt32();
+ var fieldIndex = reader.ReadInt32();
+ var c = reader.ReadInt32();
+ var valueIndex = reader.ReadInt32();
+ result[objectPosition] = new Dictionary { [fieldIndex] = valueIndex };
+ }
+
+ return result;
+ }
+
+ private static string[] ReadFieldNames(byte[] bytes, int startPosition, int numberOfFields)
+ {
+ var result = new string[numberOfFields];
+ var position = startPosition;
+ for (var i = 0; i < numberOfFields; i++)
+ {
+ var list = new List();
+ while (bytes[position] != 0)
+ {
+ list.Add(bytes[position]);
+ position++;
+ }
+
+ result[i] = Encoding.ASCII.GetString(list.ToArray());
+ position++;
+ }
+
+ return result;
+ }
+
+ private static Dictionary ReadFieldToFieldDataMapping(BinaryReader reader)
+ {
+ var length = reader.ReadInt32();
+ var result = new Dictionary();
+ for (var i = 0; i < length; i++)
+ {
+ var x = reader.ReadInt32();
+ var y = reader.ReadInt32();
+ var index = x * y;
+ var link = reader.ReadInt32();
+ result[index] = link;
+ }
+
+ return result;
+ }
+
+ private static Dictionary ReadObjectClasses(
+ BinaryReader reader,
+ int numberOfObjects)
+ {
+ var result = new Dictionary();
+ reader.ReadBytes(24);
+ for (var i = 0; i < numberOfObjects; i++)
+ {
+ var classId = reader.ReadInt32();
+ reader.ReadBytes(8);
+ var embeddedObjectPosition = reader.ReadInt32();
+ var objectPosition = reader.ReadInt32();
+ var loadingOrder = reader.ReadInt32();
+ result[i + 1] =
+ new ObjectClassInformation(embeddedObjectPosition, objectPosition, loadingOrder, classId);
+ }
+
+ return result;
+ }
+
+ private static Dictionary> ReadObjectPositionsToValuesMapping(
+ BinaryReader reader,
+ int numberOfObjects)
+ {
+ var result = new Dictionary>();
+ reader.ReadBytes(8);
+ for (var objectPosition = 1; objectPosition <= numberOfObjects; objectPosition++)
+ {
+ result[objectPosition] = ReadFieldToFieldDataMapping(reader);
+ var position = reader.BaseStream.Position;
+ if (position % 8 != 0)
+ {
+ reader.ReadBytes(8 - (int)(position % 8));
+ }
+ }
+
+ return result;
+ }
+
private static (int[] offsets, int newPosition) ReadOffsets(byte[] bytes, int startPosition)
{
var position = startPosition;
@@ -236,29 +299,52 @@ namespace MatFileHandler
break;
}
+
offsets.Add(next);
}
return (offsets.ToArray(), position);
}
- private static string[] ReadFieldNames(byte[] bytes, int startPosition, int numberOfFields)
+ private static IArray TransformOpaqueData(IArray array, SubsystemData subsystemData)
{
- var result = new string[numberOfFields];
- var position = startPosition;
- for (var i = 0; i < numberOfFields; i++)
+ if (array is MatNumericalArrayOf uintArray)
{
- var list = new List();
- while (bytes[position] != 0)
+ if (uintArray.Data[0] == 3707764736u)
{
- list.Add(bytes[position]);
- position++;
+ var (dimensions, indexToObjectId, classIndex) = DataElementReader.ParseOpaqueData(uintArray.Data);
+ return new OpaqueLink(
+ uintArray.Name,
+ string.Empty,
+ string.Empty,
+ dimensions,
+ array as DataElement,
+ indexToObjectId,
+ classIndex,
+ subsystemData);
}
- result[i] = Encoding.ASCII.GetString(list.ToArray());
- position++;
}
- return result;
+ return array;
+ }
+
+ private struct ObjectClassInformation
+ {
+ public ObjectClassInformation(int embeddedObjectPosition, int objectPosition, int loadingOrder, int classId)
+ {
+ EmbeddedObjectPosition = embeddedObjectPosition;
+ ObjectPosition = objectPosition;
+ LoadingOrder = loadingOrder;
+ ClassId = classId;
+ }
+
+ public int ClassId { get; }
+
+ public int EmbeddedObjectPosition { get; }
+
+ public int LoadingOrder { get; }
+
+ public int ObjectPosition { get; }
}
}
-}
+}
\ No newline at end of file