How to serialize object with stream in C# - c#

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.

Related

Correctly create a json file in c#

I'm currently setting-up my application, and i need to manage a json file (that contain some settings) in my windows form. Once you open it, you can easy choose different settings, and once you've done, you can save it (which mean i need to overwrite the existing one json file settings, and replace it with new one!
I tried to follow this guide for correctly create my json file! But i met 2 problems:
This solution mentioned up, create square brackets (which i don't need!)
Seems to create all settings on one line. Is that correct or it could make some problem in the future?
Some materials....
My application:
Original json file:
Json file generated with my code:
My class:
public class data
{
public bool avx { get; set; }
public bool memory_pool { get; set; }
public bool smt { get; set; }
public bool spectre { get; set; }
public bool unlock_menu { get; set; }
public bool vinput { get; set; }
public double cpu_memory_pool_fraction { get; set; }
public double gpu_memory_pool_fraction { get; set; }
}
My code:
private void btn_save_Click(object sender, EventArgs e)
{
string settings_path = general_path + "\\plugins\\cyber_engine_tweaks\\" + "config.json"; //path
bool avx_set = cb_avx.Checked;
bool smt_set = cb_smt.Checked;
bool memory_set = cb_memory.Checked;
bool spectre_set = cb_spectre.Checked;
bool debug_set = cb_debug.Checked;
bool vinput_set = cb_vinput.Checked;
List<data> _data = new List<data>();
_data.Add(new data()
{
avx = avx_set,
cpu_memory_pool_fraction = 0.5,
gpu_memory_pool_fraction = 1.0,
memory_pool = memory_set,
smt = smt_set,
spectre = spectre_set,
unlock_menu = debug_set,
vinput = vinput_set
});
using (StreamWriter file = File.CreateText(settings_path))
{
JsonSerializer serializer = new JsonSerializer();
//serialize object directly into file stream
serializer.Serialize(file, _data);
}
}
Square brackets are because you send an array:
Instead of
List<data> _data = new List<data>(); _data.Add(new data()...
try
var data = new data()... serializer.Serialize(file, data)
All settings on one line is normal.
Very nice detailed answer here: Can JSON start with "["?
TLDR:
It's not a Json without either {} indicating an object or [] indicating an array. So no, you can't have a json with multiple keys without one.
The newlines are optional in Json. Since most json objects are used for transfering over the wire, there is no need for newlines (which take up unessecary bytes).
Issues:
1 . This solution mentioned up, create square brackets (which i don't need!)
Solution:
Create your data object like:
//List<data> _data = new List<data>();
data _data = new data
{
avx = avx_set,
cpu_memory_pool_fraction = 0.5,
gpu_memory_pool_fraction = 1.0,
memory_pool = memory_set,
smt = smt_set,
spectre = spectre_set,
unlock_menu = debug_set,
vinput = vinput_set
};
2 . Seems to create all settings on one line. Is that correct or it could make some problem in the future?
Solution:
I think it's a format issue. Can igonore

Object de-serializing from base64 in C#

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 );

Json.Net does not handle deserializing a null DataTable

I have this class as object to serialize:
public class JsonServerToClient
{
public JsonServerToClient()
{
query = new List<Query>();
}
public String authKey { get; set; }
public List<Query> query { get; set; }
public struct Query
{
public int error { get; set; }
public DataTable result { get; set; }
}
}
I use JsonConvert.SerializeObject(objectAbove); to serialize JSON.
and JsonConvert.DeserializeObject<JsonServerToClient>(text); to deserialize it.
All works fine when the result is full of data, but if it is null I get this JSON:
{
"authKey": "pippo",
"query": [
{
"error": -1,
"result": null
}
]
}
The problem is when I try to deserialize this I get an exception:
Eccezione non gestita di tipo 'Newtonsoft.Json.JsonSerializationException' in Newtonsoft.Json.dll
Ulteriori informazioni: Unexpected end when deserializing array. Path '', line 1, position 56.
UPDATE
This issue was fixed in Json.Net version 8.0.1. The workaround below is no longer needed.
It appears that the DataTableConverter that ships with Json.Net (as of v6.0.3) does not handle the case where the data table itself is null. You can work around this issue by subclassing the DataTableConverter and overriding the ReadJson method like this:
class CustomDataTableConverter : DataTableConverter
{
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
if (reader.TokenType == JsonToken.Null)
{
return null;
}
return base.ReadJson(reader, objectType, existingValue, serializer);
}
}
When you deserialize, be sure to specify the custom converter:
var obj = JsonConvert.DeserializeObject<JsonServerToClient>(json,
new CustomDataTableConverter());
Which version of Json.NET are you using? As per this post, it seems to be fixed with Json.NET 3.5r6
I have same issues using Json.Net to parse DateTime field from the json response. I tried the following Json Helper class and it handled the datetime field properly.public class JsonHelper
{
/// <summary>
/// JSON Serialization
/// </summary>
public static string JsonSerializer<T>(T t)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
MemoryStream ms = new MemoryStream();
ser.WriteObject(ms, t);
string jsonString = Encoding.UTF8.GetString(ms.ToArray());
ms.Close();
return jsonString;
}
/// <summary>
/// JSON Deserialization
/// </summary>
public static T JsonDeserialize<T>(string jsonString)
{
DataContractJsonSerializer ser = new DataContractJsonSerializer(typeof(T));
MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(jsonString));
T obj = (T)ser.ReadObject(ms);
return obj;
}

JSON Deserialization in C# for multiple values

I have a JSON object like this...
{
"totalcount":1,
"files":[
{
"filename":"1.txt",
"fileContent":"Dineshkumar"
}
]
}
I have created the following classes in C#.
public class File
{
public string filename { get; set; }
public string fileContent { get; set; }
}
public class JSONObject
{
public int totalcount { get; set; }
public List<File> files { get; set; }
}
I have used the following objects to access the JSON object.
JavaScriptSerializer JSSfile = new JavaScriptSerializer();
JSSfile.MaxJsonLength = Int32.MaxValue;
JSONObject Content = JSSfile.Deserialize<JSONObject>(response);
Now my problem is.. when I have more than 1 files in the JSON object, it works perfectly fine as expected. When I have only one file in the JSON object, it returns me 0 files in content.
How to solve this issue?
When 1 file is given as JSON object, the Content variable values starts from 0.
If write this snippet for solving this issue,
if (Content.totalcount == 1)
{
File file = null;
file.filename = Content.files[0].filename;
file.fileContent = Content.files[0].fileContent;
File.WriteAllBytes(DestLocTxt.Text.Trim() + "\\" + file.filename, file.fileContent));
}
I got the following error:
An unhandled exception of type 'System.ArgumentOutOfRangeException' occurred in mscorlib.dll
Additional information: Index was out of range. Must be non-negative and less than the size of the collection.
Problem Solved:
{
"totalcount":1,
"files":[
{
"filename":"1.txt",
"fileContent":"Dineshkumar"
}
]
}
was the expected JSON but the server is sending the data in different format.
{
"totalcount":1,
"files": {
"filename":"1.txt",
"fileContent":"Dineshkumar"
}
}
All the problem caused due to this...
The issue here is not with the JSON - it's with the File object you are creating. In the example you gave, you are trying to set the filename of file even though it is null. Ensure the object is actually instantiated before trying to set properties on it. The Content object is fine, and the files list does actually contain one file object.
This is your example, with the fix in place:
var json = "{\"totalcount\":1,\"files\":[{\"filename\":\"1.txt\",\"fileContent\":\"Dineshkumar\"}]}";
JavaScriptSerializer JSSfile = new JavaScriptSerializer();
JSSfile.MaxJsonLength = Int32.MaxValue;
JSONObject Content = JSSfile.Deserialize<JSONObject>(json);
if (Content.totalcount == 1)
{
File file = new File(); //CREATE A NEW FILE OBJECT HERE <------
file.filename = Content.files[0].filename;
file.fileContent = Content.files[0].fileContent;
}
Just edit your JSONObject-class like this. Than it should work:
public class JSONObject
{
public int totalcount { get; set; }
public File files { get; set; }
}

Empty serialized XML file

i get an empty xmlfile after serializing an object. I'm using Monodevelop and Unity 4. I searched for such a long time mostly in this community, but i only found difficult problems with even more difficult answers :) I think mine is so simple, please help me. (I am new to c#)
The serialized object is this:
[System.Serializable]
public class information {
private string data1;
private string data2;
private string data3;
public void Data1(string text)
{
data1 = text;
}
public string GetData1 ()
{
return data1;
}
public void Data2(string text)
{
data2 = text;
}
public string GetData2 ()
{
return data2;
}
public void Data3(string text)
{
data3 = text;
}
}
the serializing class is this, here might be the problem:
public class SaveXml {
public void SaveData(object obj, string filename)
{
XmlSerializer sr = new XmlSerializer(obj.GetType());
TextWriter writer = new StreamWriter(filename);
sr.Serialize(writer, obj);
writer.Close();
}
public string Load()
{
if(File.Exists("accdata.xml"))
{
XmlSerializer xs = new XmlSerializer(typeof(information));
FileStream read = new FileStream("accdata.xml",FileMode.Open, FileAccess.Read, FileShare.Read);
information info = (information)xs.Deserialize(read);
return info.GetData1();
}
else
{
return "file does not exist";
}
}
And the serializing and the serialized object get called by a menu that has this 2 buttons:
if(GUI.Button(new Rect(10,50,300,100),"Save"))
{
SaveXml saver = new SaveXml();
information infol = new information();
infol.Data1("textone");
infol.Data2("texttwo");
infol.Data3( "textthree");
saver.SaveData(infol, "accdata.xml");
}
if(GUI.Button(new Rect(500,50,300,100),"Load"))
{
SaveXml saver1 = new SaveXml();
text = saver1.Load();
}
so the variable text that is declared in the class menu, should be "textone", after i clicked the Save Button and the LoadButton. The Savebutton creates a file that is empty.
The Deserialization seems to work but of course there is no String in the data1 variable in Information so the variable in the menu called text is empty too. I get no errors and i can work with the object after serialization.
So why doesnt my serialization work? Please help me. I excuse for my bad english and mistakes, i am new to stackoverflow.
Xml serializer serializes public fields/properties not methods. Change your methods to properties. For ex,
public string Data2
{
set { data2 = value; }
get { return data2; }
}
So your information class can be
public class Information
{
public string Data1 { get; set; }
public string Data2 { get; set; }
public string Data3 { get; set; }
}
BTW: you don't need this Serializable attribute. It is only used by BinaryFormatter
I'm not sure but from what i see you don't have any public fields... Take a look here
And also, why don't you just use auto getter/setter ?
According to this MSDN support article, using XmlSerializer the way you have performs only "shallow" serialization - it only serializes public fields/properties. To serialize private data requires "deep" serialization which appears to be a whole other animal.

Categories