Array mismatch on Deserialization with protobuf-net - c#

i'm a complete beginner with protobuf-net so probably this is just some stupid beginner mistake. But i cant find whats the problem with this:
I have a class thats to be serialized to disk defined like this:
[ProtoContract]
public class SerializableFactors
{
[ProtoMember(1)]
public double?[] CaF {get;set;}
[ProtoMember(2)]
public byte?[] CoF { get; set; }
}
and a Test defined like this:
if (File.Exists("factors.bin"))
{
using (FileStream file = File.OpenRead("factors.bin"))
{
_factors = Serializer.Deserialize<SerializableFactors>(file);
}
}
else
{
_factors = new SerializableFactors();
_factors.CaF = new double?[24];
_factors.CaF[8] = 7.5;
_factors.CaF[12] = 1;
_factors.CaF[18] = 1.5;
_factors.CoF = new byte?[24];
_factors.CoF[8] = 15;
_factors.CoF[12] = 45;
_factors.CoF[18] = 25;
using (FileStream file = File.Create("factors.bin"))
{
Serializer.Serialize(file, _factors);
}
}
So basically if the file doesnt exist yet i create a object with default values and serialize it to disk. If the file exists i will load it into memory.
BUT the result for me on loading the file is not what i created before the saving to disk. I create arrays with length 24 which have values in the slots 8, 12 and 18. But the deserialized object has arrays of the length 3 which hold my values.
What is my mistake in here?
Thanks in advance!

You must set the RuntimeTypeModel to support null
See the following post:
How can I persist an array of a nullable value in Protobuf-Net?
// configure the model; SupportNull is not currently available
// on the attributes, so need to tweak the model a little
RuntimeTypeModel.Default.Add(typeof(SerializableFactors), true)[1].SupportNull = true;
if (File.Exists("factors.bin"))
{
using (FileStream file = File.OpenRead("factors.bin"))
{
_factors = Serializer.Deserialize<SerializableFactors>(file);
}
}
else
{
_factors = new SerializableFactors();
_factors.CaF = new double?[24];
_factors.CaF[8] = 7.5;
_factors.CaF[12] = 1;
_factors.CaF[18] = 1.5;
_factors.CoF = new byte?[24];
_factors.CoF[8] = 15;
_factors.CoF[12] = 45;
_factors.CoF[18] = 25;
using (FileStream file = File.Create("factors.bin"))
{
Serializer.Serialize(file, _factors);
}
}

Related

ASP.NET MVC - Xml Export - formatting the file name according to class attributes

I have this class, which takes the selected file in the grid
[HttpPost]
public ActionResult ExportXml(string apontamentos)
{
try
{
string[] arrApontamentos = apontamentos.Split(';');
var listApontamentoId = arrApontamentos.Select(x => new Guid(x)).ToList();
var apontamentosViewModel = this._apontamentoAppService.ObterTodos(listApontamentoId);
List<ApontamentoExportarViewModel> listXml = new List<ApontamentoExportarViewModel>();
int item = 1;
foreach (var informacaoApontamentoVM in apontamentosViewModel)
{
listXml.Add(new ApontamentoExportarViewModel
{
Item = item,
Equipamento = informacaoApontamentoVM.Barco.SapId,
Atendimento = informacaoApontamentoVM.Atendimento,
Escala = informacaoApontamentoVM.LocalDaOperacao.Abreviacao,
DescricaoDaOperacao = informacaoApontamentoVM.CodigosDeOperacao.Descricao,
//GrupoDeCodigo = "xxx",
CodigoOperacao = informacaoApontamentoVM.CodigosDeOperacao.Codigo,
DataInicial = string.Format("{0:dd.MM.yyyy}", informacaoApontamentoVM.DataInicio),
HoraInicial = string.Format("{0:HH.mm.ss}", informacaoApontamentoVM.DataInicio),
DataFinal = string.Format("{0:dd:MM:yyyy}", informacaoApontamentoVM.DataTermino),
HoraFinal = string.Format("{0:HH:mm:ss}", informacaoApontamentoVM.DataTermino),
Observacoes = informacaoApontamentoVM.Observacao
});
item++;
}
var status = this._apontamentoAppService.ObterDescricaoStatusApontamento(Domain.Apontamentos.StatusApontamento.Exportado);
this._apontamentoAppService.AtualizarStatus(apontamentosViewModel.Select(x => x.Id).ToList(), status);
return new XmlActionResult<ApontamentoExportarViewModel>(listXml);
}
catch (Exception ex)
{
throw ex;
}
}
And a i have that other one, which does export and xml format and filename
public class XmlActionResult<T> : ActionResult
{
public XmlActionResult(List<T> data)
{
Data = data;
}
public List<T> Data { get; private set; }
public override void ExecuteResult(ControllerContext context)
{
context.HttpContext.Response.ContentType = "text/xml";
// TODO: Use your preferred xml serializer
// to serialize the model to the response stream :
// context.HttpContext.Response.OutputStream
ApontamentoExportarViewModel apontamentoExportarViewModel = new ApontamentoExportarViewModel();
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = string.Format("MA_"+ "Equipamento" + "_{0:dd-MM-yyyy_HH-mm-ss}.xml", DateTime.Now),
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
var root = new XmlRootAttribute("meadinkent");
XmlSerializer x = new XmlSerializer(Data.GetType(), root);
context.HttpContext.Response.AppendHeader("Content-Disposition", cd.ToString());
x.Serialize(context.HttpContext.Response.OutputStream, Data);
}
}
}
Basically, I need to get the "Equipamento" attribute and insert it into the file name.
The information from the "ApontamentoExportarViewModel" class is coming from the "Data" attribute, but how do you find the information inside that list? Remembering that I only need the information of the attribute "Equipment"
How would I bring the value of this attribute to the XmlActionResult class?
Well, you've got it in Data in the XmlActionResult, but because it's a List<T>, it could be anything. In this case, it's your ApontamentoExportarViewModel view model.
Should this XmlActionResult method be able to work with various kinds of objects, or only with ApontamentoExportarViewModel? If with various types, then not every one of those types will have a Equipamento property. In that case, you would have to do something like:
var fileName = "default";
if(Data is List<ApontamentoExportarViewModel>)
{
var record = (Data as List<ApontamentoExportarViewModel>).FirstOrDefault(); // what should happen if there's more than one?
if (record != null)
fileName = record.Equipamento;
}
and then later:
FileName = string.Format("MA_"+ fileName + "_{0:dd-MM-yyyy_HH-mm-ss}.xml", DateTime.Now);
Or something like that.
If you are sure that every object that comes in to your method will have a Equipamento property, then you could create a base class from which you derive your view model classes that has Equipamento, or an interface, and then alter your method to not accept any type (<T>), but rather only classes that derive from the base class / implement the interface.

Take 1gb ram to parse json object and give System.OutOfMemoryException after performing any other filter [duplicate]

This question already has answers here:
How to parse huge JSON file as stream in Json.NET?
(5 answers)
Closed 4 years ago.
public void ReadJsonFile()
{
try
{
string json = string.Empty;
using (StreamReader r = new StreamReader(val))
{
json = r.ReadToEnd();
var test = JObject.Parse(json);
JArray items = (JArray)test["locations"];
int length = items.Count;
data = new List<Info>();
for (int i = 0; i < items.Count; i++)
{
var d = test["locations"][i]["timestampMs"];
double dTimeSpan = Convert.ToDouble(d);
DateTime dtReturn = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc).AddSeconds(Math.Round(dTimeSpan / 1000d)).ToLocalTime();
string printDate = dtReturn.DayOfWeek.ToString() + "," + " " + dtReturn.ToShortDateString() + " " + dtReturn.ToShortTimeString();
day = dtReturn.DayOfWeek.ToString();
date = dtReturn.ToShortDateString();
time = dtReturn.ToShortTimeString();
var e = test["locations"][i]["latitudeE7"];
var f = test["locations"][i]["longitudeE7"];
var n = test["locations"][i]["accuracy"];
accuracy = n.ToString();
// getLocationByGeoLocation(e.ToString(), f.ToString());
var g = test["locations"][i]["activity"] != null;
if (g == true)
{
JArray items1 = (JArray)test["locations"][i]["activity"];
int length1 = items1.Count;
while (j < items1.Count)
{
if (j == 0)
{
var h = test["locations"][i]["activity"][j]["activity"][j]["type"];
type = h.ToString();
j = 1;
}
else { }
j++;
}
j = 0;
}
else { }
Info ddm = new Info(day, date, time, lat, longi, address, accuracy, type);
data.Add(ddm);
type = "";
}
}
return;
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
I am trying to parse JSON file. val is the name of my file to parse. Using StreamReader I am reading each line when I am trying to parse using jObject I will take around 1gb memory and give me System.OutOfMemoryException how can I parse JObject using small memory.
Please help me with this I don't have much idea of JSON.
Please read about JSON thoroughly. NewtonSof.JSON is are very famous library and it is well documented. Let us get back to your problem. As mentioned in comments you have lots of unnecessary middle steps while trying to parse your file. Moreover, you are trying to parse a big file in one go!. First thing first, this is the layout for your JSON
public partial class Data
{
[JsonProperty("locations")]
public Location[] Locations { get; set; }
}
public partial class Location
{
[JsonProperty("timestampMs")]
public string TimestampMs { get; set; }
[JsonProperty("latitudeE7")]
public long LatitudeE7 { get; set; }
[JsonProperty("longitudeE7")]
public long LongitudeE7 { get; set; }
[JsonProperty("accuracy")]
public long Accuracy { get; set; }
}
And while you are deserilazing you should do it object by object, not all at once
The following assumes that your stream is made of Data type of objects, if it is, made up of Location type of objects you have to change it
using (StreamReader streamReader = new StreamReader(val))
using (JsonTextReader reader = new JsonTextReader(streamReader))
{
reader.SupportMultipleContent = true;
var serializer = new JsonSerializer();
while (reader.Read())
{
if (reader.TokenType == JsonToken.StartObject)
{
var data = serializer.Deserialize<Data>(reader);
//data.locations etc etc..
}
}
}
I could fix the System.OutOfMemoryException with the following steps:
If you are not using Visual Studio Hosting Process:
Uncheck the option:
Project->Properties->Debug->Enable the Visual Studio Hosting Process
if still the problem remains:
Go to Project->Properties->Build Events->Post-Build Event Command line and paste this 2 lines
call "$(DevEnvDir)..\..\vc\vcvarsall.bat" x86
"$(DevEnvDir)..\..\vc\bin\EditBin.exe" "$(TargetPath)" /LARGEADDRESSAWARE
Now, build the project

Associate XML Attribute with Class Property

I am building an MVC App in C# and am currently working on a method to load an xml document into a file stream and iterate through the nodes and save the data into my model objects.
It is all working fine but I am hard coding the relationship between the object property and the xml attribute name. I was wondering if there is a smart way to associated the two so I can run it all through a for each loop.
This is the code I have currently and it works, but I would like to make it more generic
OLD CODE
var xmlDoc = LoadXmlFileIntoStream("WSAPayCode.xml");
var elementCollection = ExtractDescendants(xmlDoc, "WSAPayCode");
foreach (var element in elementCollection)
{
var abbreviationChar = element.Attribute("AbbreviationChar");
var payCode = new PayCode
{
Name = element.Attribute("Name").Value,
AutoResolved = element.Attribute("AutoResolved").Value.IsBool(),
EditExcuseAbsn = element.Attribute("EditExcuseAbsn").Value.IsBool(),
PersistPceSw = element.Attribute("PersistPceSw").Value.IsBool(),
AbbreviationChar = (string)element.Attribute("AbbreviationChar"),
EditCntToCdotSw = element.Attribute("EditCntToCdotSw").Value.IsBool(),
EditAffShfTotal = element.Attribute("EditAffShfTotal").Value.IsBool(),
EditCntToOt = element.Attribute("EditCntToOt").Value.IsBool(),
PayUsingWeightedAverageRate = element.Attribute("PayUsingWeightedAverageRate").Value.IsBool(),
RequiresMgrApproval = element.Attribute("RequiresMgrApproval").Value.IsBool(),
WeightedAverageRateIsComputedDaily =
element.Attribute("WeightedAverageRateIsComputedDaily").Value.IsBool(),
JustAutoResExpAsWorked = element.Attribute("JustAutoResExpAsWorked").Value.IsBool(),
AssociatedDurationPayCodeName = element.Attribute("AssociatedDurationPayCodeName").Value,
WeightedAverageRateContributionsUseAnAdjustedRate =
element.Attribute("WeightedAverageRateContributionsUseAnAdjustedRate").Value.IsBool(),
ScheduleHoursType = element.Attribute("ScheduleHoursType").Value,
CheckAvlbltySw = element.Attribute("CheckAvlbltySw").Value.IsBool(),
WageAddition = (string)element.Attribute("WageAddition"),
VisibleInMainArea = element.Attribute("VisibleInMainArea").Value.IsBool(),
IsMoneyCategory = element.Attribute("IsMoneyCategory").Value.IsBool(),
AmountType = element.Attribute("AmountType").Value,
VisibleInReport = element.Attribute("VisibleInReport").Value.IsBool(),
ContributesToWeightedAverageRates =
element.Attribute("ContributesToWeightedAverageRates").Value.IsBool(),
UnjustAutoResExpAsWorked = (bool)element.Attribute("UnjustAutoResExpAsWorked"),
WageMultiply = (string)element.Attribute("WageMultiply"),
Type = (string)element.Attribute("Type"),
VisibleToUser = (bool)element.Attribute("VisibleToUser"),
CustomerId = _customerId,
};
_db.PayCodes.Add(payCode);
_db.SaveChanges();
}
New Code
I have written some code to interate through the xml file and pull out the names of the attributes - Code Below (Also works)
var xmlDoc = LoadXmlFileIntoStream("WSAPayCode.xml");
var elementCollection = ExtractDescendants(xmlDoc, "WSAPayCode");
var nodeAttributes = xmlDoc.Descendants("WSAPayCode").Select(x => x.Attributes());
foreach (var attrs in nodeAttributes)
{
var _attribute = "";
foreach (var attr in attrs)
{
// This successfully reads through each attribute and pulls out the name of the attribut
_attribute = attr.Name.ToString();
}
}
Problem I would like to solve
What I would like to do now is instantiate an object and iterate through the attribute names and save the values to the corresponding property in the object. i.e. replace the OLD code with something that dynamically assigns the values to the object properties.
I would check into using the XML Serializers / Deserializers. On your class, you have provide attributes as to whether the property is an element or attribute, and then let it handle the rest of it for you.
You could set the attributes to match what the XML document is providing, such as if the name is an element, or attribute, etc.
For example:
[XmlRoot("PayCode")]
public class PayCode
{
[XmlElement("Name")
public string Name { get; set;}
....
}
Then, to deserialize to your object:
XmlSerializer serializer = new
XmlSerializer(typeof(PayCode));
FileStream fs = new FileStream(filename, FileMode.Open);
XmlReader reader = XmlReader.Create(fs);
PayCode payCode = (PayCode)serializer.Deserialize(reader);

Accessing bitmap array in another class? C#

I have this array :
Bitmap[] bildeListe = new Bitmap[21];
bildeListe[0] = Properties.Resources.ål;
bildeListe[1] = Properties.Resources.ant;
bildeListe[2] = Properties.Resources.bird;
bildeListe[3] = Properties.Resources.bear;
bildeListe[4] = Properties.Resources.butterfly;
bildeListe[5] = Properties.Resources.cat;
bildeListe[6] = Properties.Resources.chicken;
bildeListe[7] = Properties.Resources.dog;
bildeListe[8] = Properties.Resources.elephant;
bildeListe[9] = Properties.Resources.fish;
bildeListe[10] = Properties.Resources.goat;
bildeListe[11] = Properties.Resources.horse;
bildeListe[12] = Properties.Resources.ladybug;
bildeListe[13] = Properties.Resources.lion;
bildeListe[14] = Properties.Resources.moose;
bildeListe[15] = Properties.Resources.polarbear;
bildeListe[16] = Properties.Resources.reke;
bildeListe[17] = Properties.Resources.sheep;
bildeListe[18] = Properties.Resources.snake;
bildeListe[19] = Properties.Resources.spider;
bildeListe[20] = Properties.Resources.turtle;
I want that array and it´s content in a diffenrent class, and access it from my main form. I don´t know if should use method, function or what to use with arrays. Are there some good way for me to access for instanse bildeListe[0] in my new class?
The simplest way would be to add a property to your class to return that array. That way you always get the correct array if you happen to change it for some reason.
If you want to use a method for returning the image, don't use the other suggested method. It causes many useless objects to be created. One way is to use a static array and method.
class MYBitamp
{
static Bitmap[] bildeListe = new Bitmap[] {
Properties.Resources.ål,
Properties.Resources.ant,
Properties.Resources.bird,
Properties.Resources.bear,
Properties.Resources.butterfly,
Properties.Resources.cat,
Properties.Resources.chicken,
Properties.Resources.dog,
Properties.Resources.elephant,
Properties.Resources.fish,
Properties.Resources.goat,
Properties.Resources.horse,
Properties.Resources.ladybug,
Properties.Resources.lion,
Properties.Resources.moose,
Properties.Resources.polarbear,
Properties.Resources.reke,
Properties.Resources.sheep,
Properties.Resources.snake,
Properties.Resources.spider,
Properties.Resources.turtle
};
public static Bitmap MYarray(int index)
{
return bildeListe[index];
}
}
This way everything is initialized only once and they can be called just my MYBitmap.MYarray(2); without creating an instance of the class. I don't know if you do instantiate the class (maybe it contains something else), but there's still no problem using static here.
Put your array in a method in the class, and then create an object in your main form
class MYBitamp
{
public Bitmap MYarray (int index){
Bitmap[] bildeListe = new Bitmap[21];
bildeListe[0] = Properties.Resources.ål;
bildeListe[1] = Properties.Resources.ant;
bildeListe[2] = Properties.Resources.bird;
bildeListe[3] = Properties.Resources.bear;
bildeListe[4] = Properties.Resources.butterfly;
bildeListe[5] = Properties.Resources.cat;
bildeListe[6] = Properties.Resources.chicken;
bildeListe[7] = Properties.Resources.dog;
bildeListe[8] = Properties.Resources.elephant;
bildeListe[9] = Properties.Resources.fish;
bildeListe[10] = Properties.Resources.goat;
bildeListe[11] = Properties.Resources.horse;
bildeListe[12] = Properties.Resources.ladybug;
bildeListe[13] = Properties.Resources.lion;
bildeListe[14] = Properties.Resources.moose;
bildeListe[15] = Properties.Resources.polarbear;
bildeListe[16] = Properties.Resources.reke;
bildeListe[17] = Properties.Resources.sheep;
bildeListe[18] = Properties.Resources.snake;
bildeListe[19] = Properties.Resources.spider;
bildeListe[20] = Properties.Resources.turtle;
return bildeListe[index];
}
}
and in your main form call it with the index you want
MYBitamp aabc = new MYBitamp();
aabc.MYarray(5);

Xml deserialization appends to list

I'm trying to deserialize some settings from an xml file. The problematic property/underlying field is one called AlertColors. I initialize the underlying field to white, yellow, and red to make sure that a new instance of this class has a valid color setting. But when I deserialize, _colorArgb ends up with six values, first three are the initialization values and the last three are the ones that are read from the xml file. But the property AlertColors do not append to the field, but changes its elements. Why do I end up with a field with six colors?
Here's the code:
private List<int> _colorArgb = new List<int>(new int[] { Color.White.ToArgb(), Color.Yellow.ToArgb(), Color.Red.ToArgb() });
public List<int> AlertColors
{
get
{
return _colorArgb;
}
set
{
for (int i = 0; i < Math.Min(_colorArgb.Count, value.Count); i++)
{
if (_colorArgb[i] != value[i])
{
HasChanged = true;
}
}
_colorArgb = value;
}
}
public bool Deserialize(string filePath)
{
if (!File.Exists(filePath))
{
Logger.Log("Error while loading the settings. File does not exist.");
return false;
}
FileStream fileStream = null;
try
{
fileStream = new FileStream(filePath, FileMode.Open);
System.Xml.Serialization.XmlSerializerFactory xmlSerializerFactory =
new XmlSerializerFactory();
System.Xml.Serialization.XmlSerializer xmlSerializer =
xmlSerializerFactory.CreateSerializer(typeof(Settings));
Settings deserializedSettings = (Settings)xmlSerializer.Deserialize(fileStream);
GetSettings(deserializedSettings);
Logger.Log("Settings have been loaded successfully from the file " + filePath);
}
catch (IOException iOException)
{
Logger.Log("Error while loading the settings. " + iOException.Message);
return false;
}
catch (ArgumentException argumentException)
{
Logger.Log("Error while loading the settings. " + argumentException.Message);
return false;
}
catch (InvalidOperationException invalidOperationException)
{
Logger.Log("Error while loading the settings. Settings file is not supported." +
invalidOperationException.Message);
return false;
}
finally
{
if (fileStream != null)
fileStream.Close();
FilePath = filePath;
}
return true;
}
protected void GetSettings(Settings settings)
{
AlertColors = settings.AlertColors;
}
And the relevant part of the xml file that I'm deserializing:
<AlertColors>
<int>-1</int>
<int>-15</int>
<int>-65536</int>
</AlertColors>
Basically, that's just how XmlSerializer works. Unless the list is null, it never expects to try and set a value. In particular, most of the time, sub-item lists don't have a setter - they are things like:
private readonly List<Child> children = new List<Child>();
public List<Child> Children { get { return children; } }
(because most people don't want external callers to reassign the list; they just want them to change the contents).
Because of this, XmlSerializer operates basically like (over-simplifying):
var list = yourObj.SomeList;
foreach({suitable child found in the data})
list.Add({new item});
One fix is to use an array rather than a list; it always expects to assign an array back to the object, so for an array it is implemented more like (over-simplifying):
var list = new List<SomeType>();
foreach({suitable child found in the data})
list.Add({new item});
yourObj.SomeList = list.ToArray();
However, for a fixed number of values, a simpler implementation might be just:
public Foo Value1 {get;set;}
public Foo Value2 {get;set;}
public Foo Value3 {get;set;}
(if you see what I mean)
To get your desired result without changing your data types, you could use a DataContractSerializer (using System.Runtime.Serialization;) instead of the normal XmlSerializer. It doesn't call default constructors therefore you will end up with 3 colours instead of 6.
var ser = new DataContractSerializer(typeof(Settings));
var reader = new FileStream(#"c:\SettingsFile.xml", FileMode.Open);
var deserializedSettings = (Settings)ser.ReadObject(reader);
Coming a bit late to the party, but I just ran into this problem as well.
The accepted answer mentions that Arrays are assigned to every time a deserialization occurs. This was very helpful. But I needed a solution that didn't require me to change the type of the properties and rewrite a million lines of code. So I came up with this:
Using XML Serializer attributes you can 'redirect' the serializer to an Array that wraps around the original property.
[XmlIgnore]
public List<int> AlertColors { get; set; } = new List<int>() { Color.White.ToArgb(), Color.Yellow.ToArgb(), Color.Red.ToArgb() });
[XmlArray(ElementName = "AlertColors")]
public long[] Dummy
{
get
{
return AlertColors.ToArray();
}
set
{
if(value != null && value.Length > 0) AlertColors = new List<int>(value);
}
}
The Dummy property has to be public in order for the serializer to access it. For me however this was a small price to pay, leaving the original property unchanged so I didn't have to modify any additional code.

Categories