Consider this sample code:
var ms = new MemoryStream();
using (StreamWriter sw = new StreamWriter(ms))
using (JsonWriter writer = new JsonTextWriter(sw))
{
this.GetSerializer().Serialize(writer, data, typeof(T));
// convert stream to string
ms.Position = 0;
var reader = new StreamReader(ms);
var textToSend = reader.ReadToEnd();
}
Serializer obtained from this method:
private JsonSerializer GetSerializer()
{
var serializer = new JsonSerializer
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
ContractResolver = new CamelCasePropertyNamesContractResolver()
};
serializer.Converters.Add(new IsoDateTimeConverter { DateTimeFormat = "yyyy-MM-dd\\THH:mm:ss.fffZ" });
return serializer;
}
When I serialize 'data' it produces memory stream with 7168 bytes and when I convert to string I see how message being truncated. Searched but can't find what might be causing this limit?
Related
I have a DeepClone function using Newtonsoft:
public static T DeepClone2<T>(this T obj)
{
var jsonSettings = new JsonSerializerSettings()
{
ReferenceLoopHandling = ReferenceLoopHandling.Ignore,
TypeNameHandling = TypeNameHandling.All
};
var stringBuilder = new StringBuilder();
var stringWriter = new StringWriter(stringBuilder);
using (JsonWriter writer = new JsonTextWriter(stringWriter))
{
writer.Formatting = Formatting.None;
var serializer = JsonSerializer.Create(jsonSettings);
serializer.Serialize(writer, obj);
writer.Flush();
}
string objSerialized = stringBuilder.ToString();
T clonedObj = JsonConvert.DeserializeObject<T>(objSerialized, jsonSettings);
return clonedObj;
}
This function is exploding the stack memory when the object has a loop.
However, if I configure the ReferenceLoopHandling to Error, then it gives an error.
Why isn't the loop ignored?
I need a JSON serializer/deserializer that comes with .NET. I cannot use Newtonsoft Json.NET.
As far as I know, that leaves me with JavaScriptSerializer and DataContractJsonSerializer. JavaScriptSerializer didn't work properly, because it insists on some silly format for datetimes, which loses the UTC offset after being deserialized, nevermind that.
I am using DataContractJsonSerializer, and the test method below shows what is wrong.
I am specififying custom date format (ISO) for both serialization and deserialization, and the string looks OK. Additionally, it is readable by the Json.NET correctly.
How can I efficiently and generically workaround this issue, without doing any regexes and manual conversions?
[Test]
public void SerializerFailure()
{
TestObject testObject = new TestObject() {Dict = new Dictionary<string, object>() {{"DateTest", DateTime.UtcNow}, {"DecimalTest", 66.6M}}};
Assert.IsInstanceOf<DateTime>(testObject.Dict["DateTest"]);
string serialized = this.Serialize(testObject);
//output is OK...
//{"Dict":{"DateTest":"2019-01-07T23:16:59.5142225Z","DecimalTest":66.6}}
TestObject deserialized = this.Deserialize<TestObject>(serialized);
Assert.IsInstanceOf<string>(deserialized.Dict["DateTest"]);
TestObject newtonDeserialized = JsonConvert.DeserializeObject<TestObject>(serialized);
testObject.ShouldBeEquivalentTo(newtonDeserialized); //passes OK
testObject.ShouldBeEquivalentTo(deserialized); //Fails
//ERROR: Expected member Dict[DateTest] to be
// "2019-01-07T23:27:23.0758967Z" with a length of 28, but
// "07.01.2019 23:27:23" has a length of 19.
// Expected member Dict[DateTest] to be
// "2019-01-07T23:27:23.0758967Z", but
// "07.01.2019 23:27:23" differs near "07."(index 0).
}
Serialization:
public string Serialize(object objectToPost)
{
using (MemoryStream stream = new System.IO.MemoryStream())
{
var settings = new DataContractJsonSerializerSettings() { DateTimeFormat = new DateTimeFormat("O"), UseSimpleDictionaryFormat = true };
DataContractJsonSerializer serializer
= new DataContractJsonSerializer(objectToPost.GetType(), settings);
serializer.WriteObject(stream, objectToPost);
stream.Flush();
stream.Seek(0, SeekOrigin.Begin);
using (StreamReader reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
}
Deserialization
public T Deserialize<T>(string stringContent)
{
try
{
using (MemoryStream ms = new MemoryStream(Encoding.UTF8.GetBytes(stringContent)))
{
var settings = new DataContractJsonSerializerSettings() { DateTimeFormat = new DateTimeFormat("O"), UseSimpleDictionaryFormat = true };
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T), settings);
return (T)serializer.ReadObject(ms);
}
}
catch (Exception ex)
{
throw new InvalidOperationException($"Error while deserializing string as {typeof(T).Name}", ex);
}
}
What do first 3 bytes written by JsonSerializer on the begging of stream mean and why does DataContractJsonSerializer have a problem with them?
Sample:
Foo foo = new Foo();
using (MemoryStream stream = new MemoryStream())
{
//serialize using JsonSerializer
using (var streamWriter = new StreamWriter(stream, Encoding.UTF8, 4096, true))
using (var jsonWriter = new JsonTextWriter(streamWriter))
{
JsonSerializer jsonSerializer = JsonSerializer.Create();
jsonSerializer.Serialize(jsonWriter, foo, typeof(Foo));
}
// reset position
stream.Seek(0, SeekOrigin.Begin);
// deserialize using DataContractJsonSerializer
using (var jsonReader = JsonReaderWriterFactory.CreateJsonReader(stream, Encoding.UTF8, XmlDictionaryReaderQuotas.Max, null))
{
DataContractJsonSerializer dataContractJsonSerializer = new DataContractJsonSerializer(typeof(Foo));
foo = (Foo)dataContractJsonSerializer.ReadObject(jsonReader);
}
}
Deserialization ends up with exception: Additional information: There was an error deserializing the object of type Sandbox.Program+Foo. Encountered unexpected character 'ï'.
I seem to be getting some junk at the head of my serialized XML string. I have a simple extension method
public static string ToXML(this object This)
{
DataContractSerializer ser = new DataContractSerializer(This.GetType());
var settings = new XmlWriterSettings { Indent = true };
using (MemoryStream ms = new MemoryStream())
using (var w = XmlWriter.Create(ms, settings))
{
ser.WriteObject(w, This);
w.Flush();
return UTF8Encoding.Default.GetString(ms.ToArray());
}
}
and when I apply it to my object I get the string
<?xml version="1.0" encoding="utf-8"?>
<RootModelType xmlns:i="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.datacontract.org/2004/07/WeinCad.Data">
<MoineauPump xmlns:d2p1="http://schemas.datacontract.org/2004/07/Weingartner.Numerics">
<d2p1:Rotor>
<d2p1:Equidistance>0.0025</d2p1:Equidistance>
<d2p1:Lobes>2</d2p1:Lobes>
<d2p1:MajorRadius>0.04</d2p1:MajorRadius>
<d2p1:MinorRadius>0.03</d2p1:MinorRadius>
</d2p1:Rotor>
</MoineauPump>
</RootModelType>
Note the junk at the beginning. When I try to deserialize this
I get an error. If I copy paste the XML into my source minus
the junk prefix I can deserialize it. What is the junk text
and how can I remove it or handle it?
Note my deserialization code is
public static RootModelType Load(Stream data)
{
DataContractSerializer ser = new DataContractSerializer(typeof(RootModelType));
return (RootModelType)ser.ReadObject(data);
}
public static RootModelType Load(string data)
{
using(var stream = new MemoryStream(Encoding.UTF8.GetBytes(data))){
return Load(stream);
}
}
This fix seems to work
public static string ToXML(this object obj)
{
var settings = new XmlWriterSettings { Indent = true };
using (MemoryStream memoryStream = new MemoryStream())
using (StreamReader reader = new StreamReader(memoryStream))
using(XmlWriter writer = XmlWriter.Create(memoryStream, settings))
{
DataContractSerializer serializer =
new DataContractSerializer(obj.GetType());
serializer.WriteObject(writer, obj);
writer.Flush();
memoryStream.Position = 0;
return reader.ReadToEnd();
}
}
How do I do the equivalent in JSON.net?
public SerializedResults SerializeResults(Type queryType, IEnumerable entities)
{
var results = SerializeDynamicType(queryType);
var objList = AnonymousFns.DeconstructMany(entities, false, queryType).ToList();
var ms = new MemoryStream();
var type = objList.GetType();
var serializer = new DataContractSerializer(type);
using (ms)
{
using (GZipStream compress = new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression))
{
serializer.WriteObject(compress, objList);
}
}
results.ByteArray = ms.ToArray();
return results;
}
I am confused with this line in particular:
var serializer = new DataContractSerializer(type);
How do you do that in JSON.NET??
THANKS :-)
With JSON.NET, you don't need the type when serializing. I'm assuming that it works out the type you are passing in on its own.
So, you can get rid of this completely:
var type = objList.GetType();
var serializer = new DataContractSerializer(type);
And change this:
serializer.WriteObject(compress, objList);
To:
var json = JsonConvert.SerializeObject(objList);
Here are the JSON.Net docs for JsonConvert.
I believe you can use the BsonWriter to write to a stream. I'm not sure it will give you the exact same binary format you had before, but in concept it is the same.
public SerializedResults SerializeResults(Type queryType, IEnumerable entities)
{
var results = SerializeDynamicType(queryType);
var objList = AnonymousFns.DeconstructMany(entities, false, queryType).ToList();
var ms = new MemoryStream();
using (ms)
{
using (GZipStream compress = new GZipStream(ms, CompressionMode.Compress, CompressionLevel.BestCompression))
{
using( BsonWriter writer = new BsonWriter(compress))
{
JsonSerializer serializer = new JsonSerializer();
serializer.Serialize(writer, objList);
}
}
}
results.ByteArray = ms.ToArray();
return results;
}