I'm trying to send a Stream of data to an API through my own API. The third-party API takes an object and the object has an object property who has a property value of Stream. In my code that consumes the my API, I need to read a CSV file and then serialize the DTO object which containts the Stream property to send it to my API. My API will then just pass the DTO object to the third party API.
My issue is that I am currently serializing the Stream property as a base 64 string, but am unable to de-serialize it back into the DTO object.
DTO Object:
public class BulkLeadRequest
{
public Format3 FileFormat { get; set; }
public FileParameter FileParameter { get; set; }
public string LookupField { get; set; }
public string PartitionName { get; set; }
public int? ListId { get; set; }
public int? BatchId { get; set; }
}
Format3 is an enum:
public enum Format3
{
[System.Runtime.Serialization.EnumMember(Value = @"csv")]
Csv = 0,
[System.Runtime.Serialization.EnumMember(Value = @"tsv")]
Tsv = 1,
[System.Runtime.Serialization.EnumMember(Value = @"ssv")]
Ssv = 2,
}
FileParamter object with Stream property:
public partial class FileParameter
{
public FileParameter(System.IO.Stream data)
: this(data, null)
{
}
public FileParameter(System.IO.Stream data, string fileName)
: this(data, fileName, null)
{
}
public FileParameter(System.IO.Stream data, string fileName, string contentType)
{
Data = data;
FileName = fileName;
ContentType = contentType;
}
public System.IO.Stream Data { get; private set; }
public string FileName { get; private set; }
public string ContentType { get; private set; }
}
In order to test the serialization I have this small unit test.
public void TestSerializationDeserialization()
{
BulkLeadRequest bulkLeadRequest = new BulkLeadRequest();
bulkLeadRequest.FileFormat = Format3.Csv;
bulkLeadRequest.FileParameter = new FileParameter(new StreamReader("C:\temp\\SmallFile.csv").BaseStream);
string serializedObject = JsonConvert.SerializeObject(bulkLeadRequest, new StreamStringConverter());
var obj = JsonConvert.DeserializeObject<BulkLeadRequest>(serializedObject,
new JsonSerializerSettings {Converters = new List<JsonConverter> {new StreamStringConverter()}}); // Issue here
Assert.Equal(bulkLeadRequest, obj);
}
The StreamStringConverter:
public class StreamStringConverter : JsonConverter
{
public override bool CanConvert(Type objectType)
{
return typeof(Stream).IsAssignableFrom(objectType);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
string objectContents = (string)reader.Value;
byte[] base64Decoded = Convert.FromBase64String(objectContents);
MemoryStream memoryStream = new MemoryStream(base64Decoded);
return memoryStream;
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
FileStream valueStream = (FileStream)value;
byte[] fileBytes = new byte[valueStream.Length];
valueStream.Read(fileBytes, 0, (int)valueStream.Length);
string bytesAsString = Convert.ToBase64String(fileBytes);
writer.WriteValue(bytesAsString);
}
}
In my TestSerializationDeserialization unit test, I get the following error:
An exception of type 'Newtonsoft.Json.JsonSerializationException' occurred in Newtonsoft.Json.dll but was not handled in user code Unable to find a constructor to use for type Cocc.MarketoSvcs.Business.FileParameter. A class should either have a default constructor, one constructor with arguments or a constructor marked with the JsonConstructor attribute. Path 'FileParameter.Data', line 1, position 40.
If I add a default constructor to FileParameter then the Data property will be empty when deserialized. If I remove the implicit conversion in the JsonConvert.DeserializeObject<BulkLeadRequest>(..); call to JsonConvert.DeserializeObject(...); I will get an object, but not the BulkLeadRequest and with the base64 string still and not the Stream object I'd expect.
Serialized:
{"FileFormat":0,"FileParameter":{"Data":"RklSU1ROQU1FLE1ETElOSVQsTEFTVE5BTUUNCkNyaXN0aW5hLE0sRGlGYWJpbw0KTmVsbHksLFBhbGFjaW9zDQpNYXR0aGV3LEEsTmV2ZXJz","FileName":null,"ContentType":null},"LookupField":null,"PartitionName":null,"ListId":null,"BatchId":null}
What am I doing wrong?