I have a class as so
[Serializable]
public class ExternalAccount
{
public string Name { get;set;}
}
I have converted this to JSON like so
{\"Name\":\"XYZ\"}
I have then base64 encoded the JSON string
I then send across the wire to a web api service
I receive the base64 encoded string and now need to de-serialize it back to the original object as above (ExternalAccount) so i firstly do a
byte[] byteArray = Convert.FromBase64String(base64EncodedExternalAccount);
What is the next step?
I have tried the below but this returns null...
using (MemoryStream memoryStream = new MemoryStream(byteArrayToConvert))
{
BinaryFormatter binaryFormatter = new BinaryFormatter();
// set memory stream position to starting point
memoryStream.Position = 0;
// Deserializes a stream into an object graph and return as a object.
return binaryFormatter.Deserialize(memoryStream) as ExternalAccount;
}
Any pointers/tips greatly appreciated.
You can try converting the byte array back to string (it will be the same JSON you sent), then deserialize to the ExternalAccount object. Using the Newtonsoft JSON library the following sample correctly displays "Someone" on the console:
class Program
{
static void Main(string[] args)
{
var account = new ExternalAccount() { Name = "Someone" };
string json = JsonConvert.SerializeObject(account);
string base64EncodedExternalAccount = Convert.ToBase64String(Encoding.UTF8.GetBytes(json));
byte[] byteArray = Convert.FromBase64String(base64EncodedExternalAccount);
string jsonBack = Encoding.UTF8.GetString(byteArray);
var accountBack = JsonConvert.DeserializeObject<ExternalAccount>(jsonBack);
Console.WriteLine(accountBack.Name);
Console.ReadLine();
}
}
[Serializable]
public class ExternalAccount
{
public string Name { get; set; }
}
you need to extract string from the bytes you recieve.
byte[] byteArray = Convert.FromBase64String(base64EncodedExternalAccount);
string AccountInfo = System.Text.Encoding.UTF8.GetString(byteArray );
As expected, you will get {\"Name\":\"XYZ\"} in your AccountInfo string. Now you need to Deserialize. you can use the same model, ExternalAccount. you may do something like:
ExnternalAccount model = new System.Web.Script.Serialization.JavaScriptSerializer().Deserialize<ExnternalAccount>(AccountInfo );
Related
I have a class with a byte array as an attribute as binary
[System.Xml.Serialization.XmlAttributeAttribute(DataType="hexBinary")]
public byte[] aValue {
get {
return this.aValueField;
}
set {
this.aValueField= value;
}
}
The data itself...for aValue...has a String inside in the XML file I am attempting to deserialize in certain files
.
To deserialize I do this:
XmlSerializer xml = new XmlSerializer(typeof(Data));
using (Stream reader = new FileStream(file, FileMode.Open))
{
config = (Data)xml.Deserialize(reader);
}
The problem is, the data in the XML file, it has a String there not a byte[] (but other files do have a valid byte[] too). I cannot change the input file data nor can I change the attribute to a String, it has to be a byte[] for other files processed. Is there a way to do a custom conversion during this deserialization process somehow during via code for just this field, if the input is a String, to do a Custom Conversion to byte[] using logic? That way it doesn't exception and not get the class.
If you can differentiate between the string and the byte[] in code, you can add another property to handle the serialization, and make aValue XmlIgnore so it won't be deserialized but it will still get set inside bValue set.
private string bValueField;
[System.Xml.Serialization.XmlIgnore]
public byte[] aValue { get; set; }
[System.Xml.Serialization.XmlAttribute("aValue")]
public string bValue
{
get
{
return bValueField;
}
set
{
if (value.Contains("string identifier here")) // i.e. it's not a byte[]
{
aValue = new byte[] { };
bValueField = value;
}
else // it's a byte[]
{
var formatter = new BinaryFormatter();
using (var stream = new MemoryStream(Encoding.Unicode.GetBytes(value ?? "")))
{
aValue = (byte[])formatter.Deserialize(stream);
bValueField = "not a string";
}
}
}
}
I'm looking for a way to serialize an object with a stream to JSON to POST to an API. I continue to receive this error:
Newtonsoft.Json.JsonSerializationException: 'Error getting value from 'ReadTimeout' on 'System.IO.FileStream'.'
I understand what the error is saying but I don't understand what I need to implement to resolve it. Does it need to be converted to something else before JSON?
Here is my code:
Model:
[Serializable]
public class Document
{
public string Number { get; set; }
public string Revision { get; set; }
public string FileName { get; set; }
public Stream File { get; set; }
}
Building request:
public Document BuildRequest(string pdfFile, string txtFile)
{
Document document = new Document();
try
{
string data = File.ReadAllText(txtFile);
string[] array = data.Split('|');
FileStream fs = new FileStream(pdfFile, FileMode.Open);
document.Number = array[0];
document.Revision = array[1];
document.FileName = file;
document.File = fs;
}
// rest of code...
}
Post:
public void Post(Document document)
{
var json = JsonConvert.SerializeObject(document);
// rest of code in method...
}
After posting this I saw that this was answered already by Lasse in the comments, therefore this answer will serve as an alternative to achieving this
Here is an example which implements a custom JsonConverter that converts the File property to and from a Base64 string so it can be transferred over the network.
Some important points:
You'll need to test this code in scenarios where you have a large PDF file
You'll have to refactor this code to handle certain edge cases, should you identify any
I have written this purely to answer your question of "can it be done", I have not considered any exceptions, edge cases or even network latency depending on how large the Base64 string will become - you will experience issues and limitations depending on the content size of the HTTP request.
The API needs to know how to process this request, thus reading the Base 64 text as a stream.
Starting off, I created a StreamStringConverter
/// <summary>
/// Handles the (de)serialization of <see cref="Stream"/>.
/// </summary>
/// <remarks>
/// The <see cref="Stream"/> will be written as a Base64 encoded string, on the inverse it will be converted from a Base64 string to a <see cref="MemoryStream"/>.
/// </remarks>
public class StreamStringConverter : JsonConverter
{
private static Type AllowedType = typeof(Stream);
public override bool CanConvert(Type objectType)
=> objectType == AllowedType;
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var objectContents = (string)reader.Value;
var base64Decoded = Convert.FromBase64String(objectContents);
var memoryStream = new MemoryStream(base64Decoded);
return memoryStream;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var valueStream = (FileStream)value;
var fileBytes = new byte[valueStream.Length];
valueStream.Read(fileBytes, 0, (int)valueStream.Length);
var bytesAsString = Convert.ToBase64String(fileBytes);
writer.WriteValue(bytesAsString);
}
}
You can decorate the appropriate members in your Document class to use this custom StreamStringConverter
public class Document
{
public string Number { get; set; }
public string Revision { get; set; }
public string FileName { get; set; }
// Specify a custom JsonConverter for our StreamJsonConverter
[JsonConverter(typeof(StreamStringConverter))]
public Stream File { get; set; }
}
Your model is now ready to begin serializing and deserializing, I have updated some of the code to use string in place of an actual file handle for txtFile, for simplicity.
static void Main(string[] args)
{
Document document = new Document();
const string file = "file";
const string txtFileContents = "1|1.0";
const string pdfFile = "myPdfFile.pdf";
try
{
string[] array = txtFileContents.Split('|');
FileStream fs = new FileStream(pdfFile, FileMode.Open);
document.Number = array[0];
document.Revision = array[1];
document.FileName = file;
document.File = fs;
}
catch (Exception exception)
{
}
// Serialize the Document object
// File, in the JSON contents, will be a Base64 encoded string
var serializedContents = JsonConvert.SerializeObject(document);
// Deserialize the contents
// File will be a Stream
var deserializedContents = JsonConvert.DeserializeObject<Document>(serializedContents);
// For demo purposes, this will write the Document.File object back to a new PDF file for comparison
using (var fileStream = File.Create("myDeserializedPdfFile.pdf"))
{
var fileAsMemoryStream = (MemoryStream)deserializedContents.File;
fileAsMemoryStream.WriteTo(fileStream);
}
}
Again I reiterate that I have not written this code to be production ready, that's up to you, this is simply to guide you in the right direction.
Like the good fellows Lasse and Colin said in the comments: You can't write a filestream into Json.
If you want to send the file into json you'll need to change it to some type of string or byte array.
I'm making a simple program that receive UDP Packets from another program and I want to cast this packet to a class.
I have a class :
public class Packet
{
public string MyFirstProperty {get; set;}
public string MySecondProperty {get; set;}
public string MyThirdProperty {get; set;}
public OtherObject[] ObjectArray {get; set}
}
The packet that I receive are bytes array. How can I transform the packet to a class. I've heard of marshalling but I'm not experienced enough to fully understand it.
What should I do.
Thank you.
To send an object from client to server (utilizing Json.Net) ; assuming you have already done your own research and have a working UDP client/server
Client site:
c1) Packet p = new Packet();
c2) //fill the properties
c3) string json = JsonConvert.SerializeObject(p);
c4) byte[] buf = Encoding.UTF8.GetBytes(json);
c5) Send *buf* to server
Server site:
s1) Receive data from client( call it *buf*)
s2) string json = Encoding.UTF8.GetString(buf); (must be equal to json at client site)
s3) Packet p = JsonConvert.DeserializeObject<Packet>(json);
s4) Tada........
Now you can use the same algorithm to send object from server to client
PS: As long as you can send and then receive the same byte array using UDP(c5 => s1), you can get the original object back.
You need to use Serialization and De-Serialization to convert back class objects from and to Byte Array.
Here is a sample example.
public class MyClass {
public int Id { get; set; }
public string Name { get; set; }
public byte[] Serialize() {
using (MemoryStream m = new MemoryStream()) {
using (BinaryWriter writer = new BinaryWriter(m)) {
writer.Write(Id);
writer.Write(Name);
}
return m.ToArray();
}
}
public static MyClass Desserialize(byte[] data) {
MyClass result = new MyClass();
using (MemoryStream m = new MemoryStream(data)) {
using (BinaryReader reader = new BinaryReader(m)) {
result.Id = reader.ReadInt32();
result.Name = reader.ReadString();
}
}
return result;
}
}
Link to MSDN for more on serialization
i have an object of following class
[XmlRoot("http://schemas.abc.com")]
[DataContract]
public class Employee
{
[XmlAttribute]
[DataMember]
public List<string> Positions { get; set; }
}
Positions contains following two strings "Project Manager" and "Senior Project Manager"
I serialized this object through following method and saved in DB
public static string Serialize(Employee entity)
{
var memoryStream = new MemoryStream();
var serializer = new XmlSerializer(typeof(Employee));
using (var xmlTextWriter = new XmlTextWriter(memoryStream,
Encoding.Unicode))
{
serializer.Serialize(xmlTextWriter, entity);
xmlTextWriter.Close();
}
return UnicodeByteArrayToString(memoryStream.ToArray());
}
private static string UnicodeByteArrayToString(byte[] input)
{
var constructedString = Encoding.Unicode.GetString(input);
return constructedString;
}
Then i am retrieving and deserializing the object through following method
public static Employee Deserialize(string str)
{
var data = StringToUnicodeByteArray(str);
var memoryStream = new MemoryStream(data);
var serializer = new XmlSerializer(typeof(Employee));
var xmlTextReader = new XmlTextReader(memoryStream);
return (Employee)serializer.Deserialize(xmlTextReader);
}
private static byte[] StringToUnicodeByteArray(string input)
{
var byteArray = Encoding.Unicode.GetBytes(input);
return byteArray;
}
Now i am getting 5 objects of strings in position list
"Project", "Manager", "Senior", "Project", "Manager"
deserialization is creating new string object after every space
Quick help will be much appreciated
Answer from Eugene Podskal
think that you should remove the XmlAttribute attribute. XmlSerializer doesn't know how to parse values in attribute string other than by splitting it on whitespace. Or you can create a property that will give you String that allows to unambiguously separate individual items in it (like with commas or whatever), and that can parse individual items from the String it is set to. This propery will be serializable, while actual List will be XmlIgnore
I have 2 clases:
public class products
{
public string category;
public string name;
public double price;
public string desc;
public string version;
public string logoURL;
public string imgURL;
public string prod;
public string Category
{
set { categorie = value; }
get { return category; }
}
and:
[Serializable()]
public class groupProducts
{
public products[] produse;
}
I want to XmlSerialize the groupProducts class and send the data from a server via TCP conection to a client!
I've tried something like:
groupProducts gp = new groupProducts();
XmlSerializer xmlSel = new XmlSerializer(typeof(groupProducts));
TextWriter txtStream = new StreamWriter("xmlStreamFile.xml");
xmlSel.Serialize(txtStream, gp);
txtStream.Close();
try
{
Stream inputStream = File.OpenRead("xmlStreamFile.xml");
// declaring the size of the byte array to the length of the xmlfile
msg = new byte[inputStream.Length];
//storing the xml file in the byte array
inputStream.Read(msg, 0, (int)inputStream.Length);
//reading the byte array
communicator[i].Send(msg);
}
but it when I Deserialize it on the client side - the XML file has some weird data in it!
Do you have any idea what could it be? What am I doing wrong?
1- To be safe I would use an encoding while opening StreamWriter
2- In inputStream.Read(msg, 0, (int)inputStream.Length); Read does't guarantee that you will get inputStream.Length bytes from stream. You have to check the returned value.
3- You don't need a temp file. Use MemoryStream
XmlSerializer xmlSel = new XmlSerializer(typeof(groupProducts));
MemoryStream m = new MemoryStream();
xmlSel.Serialize(m);
communicator[i].Send(m.ToArray());