object to byte not working - c#

I am trying to convert class to byte but not worked.
I receive a byte[] (b) with zero length . why ?
void start()
{
Item item = new Item();
item.files.Add(#"test");
byte[] b = ObjectToByteArray(item); // b will have zero length
}
[Serializable]
public class Item : ISerializable
{
public Item()
{
files = new List<string>();
Exclude = false;
CleanEmptyFolder = false;
}
public List<string> files;
public string MusicProfileName;
public bool Exclude;
#region ISerializable Members
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("files", files);
info.AddValue("MusicProfileName", MusicProfileName);
info.AddValue("Exclude", Exclude);
}
#endregion
}
public byte[] ObjectToByteArray(object _Object)
{
using (var stream = new MemoryStream())
{
// serialize object
var formatter = new BinaryFormatter();
formatter.Serialize(stream, _Object);
// get a byte array
var bytes = new byte[stream.Length];
using (BinaryReader br = new BinaryReader(stream))
{
bytes = br.ReadBytes(Convert.ToInt32(stream.Length));
}
return bytes;
}
}

You didn't reset the MemoryStream before you started to read from it. The position is at the end of the stream, so you get an empty array.
Use the ToArray method instead, it gets the entire content regardless of the current position:
public byte[] ObjectToByteArray(object _Object) {
using (var stream = new MemoryStream()) {
// serialize object
var formatter = new BinaryFormatter();
formatter.Serialize(stream, _Object);
// get a byte array
return stream.ToArray();
}
}

Try using MemoryStream.ToArray instead of trying to read from the MemoryStream with a BinaryReader:
return stream.ToArray();
The problem is that you aren't resetting the stream, so it's trying to read from the end instead of the beginning. You could also seek back to the beginning of the stream:
stream.Seek(0, SeekOrigin.Begin);

Related

Cannot deserialize an object collection from a loaded XML file

Currently I creating a save\load system for my game. Its purpose is to be able to convert a Dictionary<string, object> to an encrypted string, and write it to disk file, and vice versa.
Saving functionality works just fine as follows:
public void Save()
{
PrepareSavableData();
using StreamWriter writer = new(SavegamePath);
string rawData = AuxMath.SerializeObjectToString(_storedStates);
string encodedData = AuxMath.Encode(rawData, PublicKey, PrivateKey);
writer.Write(encodedData);
writer.Close();
SavegameCompleted?.Invoke(this, EventArgs.Empty);
}
private void PrepareSavableData()
{
foreach (var entity in _registeredEntities)
{
_storedStates[entity.ID] = entity.GetState();
}
}
I fetch the data from every registered SavableEntitiy as follows:
public object GetState()
{
List<object> states = new(_savables.Count);
foreach (var savable in _savables)
{
states.Add(savable.CaptureState());
}
return states;
}
In the Save() above I use DES algorithm to encrypt data that is previously serialized to a string. No problems with that as well - I tested it thoroughly.
public static string SerializeObjectToString(object obj)
{
using MemoryStream memoryStream = new();
using StreamReader reader = new(memoryStream, Encoding.UTF8);
DataContractSerializer serializer = new(obj.GetType());
serializer.WriteObject(memoryStream, obj);
memoryStream.Position = 0;
return reader.ReadToEnd();
}
public static string Encode(string input, string publicKey, string privateKey)
{
try
{
byte[] inputAsBytes = Encoding.UTF8.GetBytes(input);
byte[] publicKeyAsBytes = Encoding.UTF8.GetBytes(publicKey);
byte[] privateKeyAsBytes = Encoding.UTF8.GetBytes(privateKey);
using DESCryptoServiceProvider cryptoServiceProvider = new();
using MemoryStream memoryStream = new();
using CryptoStream cryptoStream = new(memoryStream,
cryptoServiceProvider.CreateEncryptor(publicKeyAsBytes, privateKeyAsBytes),
CryptoStreamMode.Write);
cryptoStream.Write(inputAsBytes, 0, inputAsBytes.Length);
cryptoStream.FlushFinalBlock();
return Convert.ToBase64String(memoryStream.ToArray());
}
catch (Exception e)
{
return e.Message;
}
}
So fetching the savable data, serializing it, encoding, and writing to a disc file works without a hitch. At the game startup save file loading is performed, decoding, and deserialization, so that later any SavableEntity which registers itself can restore its previous state if one exists in the state dictionary. Reading the save file and decoding it works just fine.
private bool Load()
{
if (File.Exists(SavegamePath))
{
try
{
using StreamReader reader = new(SavegamePath);
string encodedData = reader.ReadToEnd();
string decodedData = AuxMath.Decode(encodedData, PublicKey, PrivateKey);
_storedStates = AuxMath.DeserializeStringToObject<Dictionary<string, object>>(decodedData, _knownSavableTypes);
SavegameLoadSuccesful?.Invoke(this, EventArgs.Empty);
return true;
}
catch (Exception e)
{
SavegameLoadFailed?.Invoke(this, new SavegameLoadFailedEventArgs(e.Message));
return false;
}
}
return false;
}
public static string Decode(string encodedInput, string publicKey, string privateKey)
{
try
{
byte[] encodedInputAsBytes = Convert.FromBase64String(encodedInput);
byte[] publicKeyAsBytes = Encoding.UTF8.GetBytes(publicKey);
byte[] privateKeyAsBytes = Encoding.UTF8.GetBytes(privateKey);
using DESCryptoServiceProvider cryptoServiceProvider = new();
using MemoryStream memoryStream = new();
using CryptoStream cryptoStream = new(memoryStream,
cryptoServiceProvider.CreateDecryptor(publicKeyAsBytes, privateKeyAsBytes),
CryptoStreamMode.Write);
cryptoStream.Write(encodedInputAsBytes, 0, encodedInputAsBytes.Length);
cryptoStream.FlushFinalBlock();
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
catch (Exception e)
{
return e.Message;
}
}
But, when it finally comes to the data deserialization phase from a successfully loaded and decoded file, an error occurs inside exactly this deserialization method right inside the if statement. DataContractSerializer cannot properly deserialize a states dictionary from a memory stream.
public static T DeserializeStringToObject<T>(string objectAsXml, IEnumerable<Type> knownTypes)
{
using MemoryStream memoryStream = new();
using StreamWriter writer = new(memoryStream, Encoding.UTF8);
DataContractSerializer deserializer = new(typeof(T), knownTypes);
writer.Write(objectAsXml);
memoryStream.Position = 0;
if (deserializer.ReadObject(memoryStream) is T value)
{
return value;
}
else
{
throw new Exception("Passed data is invalid or corrupted and cannot be properly restored!");
}
}
Currently this error occurs:
XmlException: Unexpected end of file. Following elements are not closed: Value, KeyValueOfstringanyType, ArrayOfKeyValueOfstringanyType. Line 1, position 197.
It may be something trivial, but I wasn't able to crack it on my own so far. Please, help!
Best regards, Vyacheslav.

Value already read, or no value when trying to read from a Stream

I've been trying this for a long time but it keeps giving me an error. I have an array of bytes that should represent a nbt document. I would like to convert this into a c# object with a library: fNbt.
Here is my code:
byte[] buffer = Convert.FromBase64String(value);
byte[] decompressed;
using (var inputStream = new MemoryStream(buffer))
{
using var outputStream = new MemoryStream();
using (var gzip = new GZipStream(inputStream, CompressionMode.Decompress, leaveOpen: true))
{
gzip.CopyTo(outputStream);
}
fNbt.NbtReader reader = new fNbt.NbtReader(outputStream, true);
var output = reader.ReadValueAs<AuctionItem>(); //Error: Value already read, or no value to read.
return output;
}
When I try this, it works:
decompressed = outputStream.ToArray();
outputStream.Seek(0, SeekOrigin.Begin);
outputStream.Read(new byte[1000], 0, decompressed.Count() - 1);
But when I try this, it doesn't:
outputStream.Seek(0, SeekOrigin.Begin);
fNbt.NbtReader reader = new fNbt.NbtReader(outputStream, true);
reader.ReadValueAs<AuctionItem>();
NbtReader, like most stream readers, begins reading from the current position of whatever stream you give it. Since you're just done writing to outputStream, then that position is the stream's end. Which means at that point there's nothing to be read.
The solution is to seek the outputStream back to the beginning before reading from it:
outputStream.Seek(0, SeekOrigin.Begin); // <-- seek to the beginning
// Do the read
fNbt.NbtReader reader = new fNbt.NbtReader(outputStream, true);
var output = reader.ReadValueAs<AuctionItem>(); // No error anymore
return output;
The solution is as follows. NbtReader.ReadValueAs does not consider a nbtCompound or nbtList as value. I made this little reader but it is not done yet (I will update the code once it is done).
public static T ReadValueAs<T>(string value) where T: new()
{
byte[] buffer = Convert.FromBase64String(value);
using (var inputStream = new MemoryStream(buffer))
{
using var outputStream = new MemoryStream();
using (var gzip = new GZipStream(inputStream, CompressionMode.Decompress, leaveOpen: true))
{
gzip.CopyTo(outputStream);
}
outputStream.Seek(0, SeekOrigin.Begin);
return new EasyNbt.NbtReader(outputStream).ReadValueAs<T>();
}
}
This is the NbtReader:
private MemoryStream MemStream { get; set; }
public NbtReader(MemoryStream memStream)
{
MemStream = memStream;
}
public T ReadValueAs<T>() where T: new()
{
return ReadTagAs<T>(new fNbt.NbtReader(MemStream, true).ReadAsTag());
}
private T ReadTagAs<T>(fNbt.NbtTag nbtTag)
{
//Reads to the root and adds to T...
}

Converting ImageSource to Object Throws Binary stream error

I'm trying to convert/cast an image source to an object. I tried to convert to a byte prior to object.
ImageSource oImageSelectedByUser = (from ii in mySource where ii.PhotoImgStream != null select ii.PhotoImgStream).FirstOrDefault();
var oImageAsByte = await ConvertStreamtoByteAsync(oImageSelectedByUser);
var oImageAsObject = FromByteArray<object>(oImageAsByte);
private static async Task<byte[]> ConvertStreamtoByteAsync(ImageSource imageSource)
{
byte[] buffer = new byte[16 * 1024];
try
{
if (imageSource is FileImageSource)
{
FileImageSource objFileImageSource = (FileImageSource)imageSource;
string strFileName = objFileImageSource.File;
var webClient = new WebClient();
buffer = await webClient.DownloadDataTaskAsync(new Uri(strFileName));
return buffer;
}
}
catch (Exception ex)
{
buffer = null;
}
return buffer;
}
public T FromByteArray<T>(byte[] data)
{
if (data == null)
return default(T);
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(data))
{
object obj = bf.Deserialize(ms);
return (T)obj;
}
}
When trying to convert to object, the error I get is:
Binary stream '0' does not contain a valid BinaryHeader. Possible
causes are invalid stream or object version change between
serialization and deserialization.
I wanted to ask is there a better way to do this?
Any direction/pointers appreciated.
You could modify the code like following
public T FromByteArray<T>(byte[] data)
{
if (data == null)
return default(T);
BinaryFormatter bf = new BinaryFormatter();
using (MemoryStream ms = new MemoryStream(data))
{
ms.Seek(0, SeekOrigin.Begin); // /rewinded the stream to the begining.
object obj = bf.Deserialize(ms);
return (T)obj;
}
}

How to merge two memory streams?

I have two MemoryStream instances.
How to merge them into one instance?
Well, now I can't copy from one MemoryStream to another.
Here is a method:
public static Stream ZipFiles(IEnumerable<FileToZip> filesToZip) {
ZipStorer storer = null;
MemoryStream result = null;
try {
MemoryStream memory = new MemoryStream(1024);
storer = ZipStorer.Create(memory, GetDateTimeInRuFormat());
foreach (var currentFilePath in filesToZip) {
string fileName = Path.GetFileName(currentFilePath.FullPath);
storer.AddFile(ZipStorer.Compression.Deflate, currentFilePath.FullPath, fileName,
GetDateTimeInRuFormat());
}
result = new MemoryStream((int) storer.ZipFileStream.Length);
storer.ZipFileStream.CopyTo(result); //Does not work!
//result's length will be zero
}
catch (Exception) {
}
finally {
if (storer != null)
storer.Close();
}
return result;
}
Spectacularly easy with CopyTo or CopyToAsync:
var streamOne = new MemoryStream();
FillThisStreamUp(streamOne);
var streamTwo = new MemoryStream();
DoSomethingToThisStreamLol(streamTwo);
streamTwo.CopyTo(streamOne); // streamOne holds the contents of both
The framework, people. The framework.
Based on the answer shared by #Will above, here is complete code:
void Main()
{
var s1 = GetStreamFromString("Hello");
var s2 = GetStreamFromString(" World");
var s3 = s1.Append(s2);
Console.WriteLine(Encoding.UTF8.GetString((s3 as MemoryStream).ToArray()));
}
public static Stream GetStreamFromString(string text)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(text);
writer.Flush();
stream.Position = 0;
return stream;
}
public static class Extensions
{
public static Stream Append(this Stream destination, Stream source)
{
destination.Position = destination.Length;
source.CopyTo(destination);
return destination;
}
}
And merging stream collection with async:
async Task Main()
{
var list = new List<Task<Stream>> { GetStreamFromStringAsync("Hello"), GetStreamFromStringAsync(" World") };
Stream stream = await list
.Select(async item => await item)
.Aggregate((current, next) => Task.FromResult(current.Result.Append(next.Result)));
Console.WriteLine(Encoding.UTF8.GetString((stream as MemoryStream).ToArray()));
}
public static Task<Stream> GetStreamFromStringAsync(string text)
{
MemoryStream stream = new MemoryStream();
StreamWriter writer = new StreamWriter(stream);
writer.Write(text);
writer.Flush();
stream.Position = 0;
return Task.FromResult(stream as Stream);
}
public static class Extensions
{
public static Stream Append(this Stream destination, Stream source)
{
destination.Position = destination.Length;
source.CopyTo(destination);
return destination;
}
}
Create third(let it be mergedStream) MemoryStream with length
equal to sum of first and second lengths
Write first MemoryStream to mergedStream (use GetBuffer() to
get byte[] from MemoryStream)
Write second MemoryStream to mergedStream(use GetBuffer())
Remember about offset while writing.
It's rather append, but it's totally unclear what is merge operation on MemoryStreams

Why StringWriter.ToString return `System.Byte[]` and not the data?

UnZipFile method writes the data from inputStream to outputWriter.
Why sr.ToString() returns System.Byte[] and not the data?
using (var sr = new StringWriter())
{
UnZipFile(response.GetResponseStream(), sr);
var content = sr.ToString();
}
public static void UnZipFile(Stream inputStream, TextWriter outputWriter)
{
using (var zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if ((currentEntry = zipStream.GetNextEntry()) != null)
{
var size = 2048;
var data = new byte[size];
while (true)
{
size = zipStream.Read(data, 0, size);
if (size > 0)
{
outputWriter.Write(data);
}
else
{
break;
}
}
}
}
}
The problem is on the line:
outputWriter.Write(data);
StringWriter.Write has no overload expecting a byte[]. Therefore, Write(Object) is called instead. And according to MSDN:
Writes the text representation of an object to the text string or stream by calling the ToString method on that object.
Calling ToString on a byte array returns System.byte[], explaining how you get that string in your StringWriter.
The reason is simple:
data is of type byte[]. There is no overload for byte[] on StringWriter so it uses the overload for object. And then calls ToString() on the boxed byte array which simply prints the type.
Your code is equivalent to this:
outputWriter.Write(data.ToString());
theateist,
Looking at the other answers here, I am going to have to agree that the reason for the "ToString()" returning System.Byte[] is because that is what you are putting into it, and everything put into the StringWriter calls it's own "ToString" method when doing so. (i.e. byte[].toString() = "System.byte[]"). In fact the whole idea is that the StringWriter is only ment for writing into a string "buffer" (StringBuilder), so in theory if your file was large enough(bigger than 2048), your output would be "System.Byte[]System.Byte[]" (etc.). Try this to deflate into a memory stream and then read from that stream, may be a better understanding of what you are looking at. (Code not tested, just example).
using (Stream ms = new MemoryStream())
{
UnZipFile(response.GetResponseStream(), ms);
string content;
ms.Position = 0;
using(StreamReader s = new StreamReader(ms))
{
content = s.ReadToEnd();
}
}
public static void UnZipFile(Stream inputStream, Stream outputWriter)
{
using (var zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if ((currentEntry = zipStream.GetNextEntry()) != null)
{
int size = 2048;
byte[] data = new byte[size];
while (true)
{
size = zipStream.Read(data, 0, size);
if (size > 0)
{
outputWriter.Write(data);
}
else
{
break;
}
}
}
}
}
Another idea would actually be to using the endcoding to get the string
public string UnZipFile(Stream inputStream)
{
string tmp;
using(Stream zipStream = new ZipInputStream(inputStream))
{
ZipEntry currentEntry;
if(currentEntry = zipStream.GetNextEntry()) != null)
{
using(Stream ms = new MemoryStream())
{
int size = 2048;
byte[] data = new byte[size];
while(true)
{
if((size = zipStream.Read(data,0,size)) > 0)
ms.Write(data);
else
break;
}
tmp = Encoding.Default.GetString(ms.ToByteArray());
}
}
}
}
return tmp;
}
Or as one last idea, you could actually change your original code to have
outputWriter.Write(Encoding.Default.GetString(data));
Instead of
outputWriter.Write(data);
By the way, please avoid the var keyword in posts, maybe just my pet peev, but code is less readable when utilizing weak types.
StringWriter.Write:MSDN
StringWriter.ToString:MSDN

Categories