I want to serialize FileInfo object using json.
Till now, I am getting below result upon serializing file info object using JsonConvert.serializeOjbect() method.
FileInfo finfo = new FileInfo("drive:\\folderpath\\file.txt");
string jsonString = JsonConvert.SerializeObject(finfo);
Of course, finfo holds a lot more properties than the two held by jsonString.
(Properties like Exists, Directory, CreationTime, Extension, IsReadonly, LastAccessTime, etc.)
jsonstring only holds two of these properties.
jsonString = {{
"OriginalPath": "drive:\\folderpath\\file.txt",
"FullPath": "drive:\\folderpath\\file.txt"
}}
Is there a way to serialize whole object instead of these two properties using Json?
The reason that you're only seeing those two properties is because FileInfo implements the ISerializable interface, and its GetObjectInfo method is being used to override the default serialization behavior. If you had full control over the FileInfo class (which you don't) then you could apply the [JsonObject] attribute to force normal Json serialization behavior.
I think your only option in this case to write your own wrapper containing the properties that you want. Your properties can just call the underlying FileInfo properties directly.
I'm trying to move forward with #RogerN comment "Your properties can just call the underlying FileInfo properties directly"
I don't see how to be able to serialize/deserialize without using a completely separate data type. Any way I can take in FileInfo for serializing and use the JSON values for deserializing? This is the best I've come up with and "Object reference not set to an instance of an object" error
void Main()
{
var res = Directory.GetFiles(#"C:\Users\kelly\OneDrive\Code\LinqPAD6 Portable\")
.Take(3)
.Select(d => new MyFileInfo2(new FileInfo(d)));
var jsonPath = #"C:\Users\Public\Documents\tst.json";
var json = System.Text.Json.JsonSerializer.Serialize(res, new JsonSerializerOptions { WriteIndented = true });
File.WriteAllText(jsonPath, json);
Thread.Sleep(100);
json = File.ReadAllText(jsonPath);
var readFromJson = System.Text.Json.JsonSerializer.Deserialize<List<MyFileInfo2>>(json);
readFromJson.Dump();
}
public class MyFileInfo2
{
private readonly FileInfo _fi;
public MyFileInfo2(FileInfo fi)
{
_fi = fi;
Path = fi.FullName;
Size = fi.Length;
}
public MyFileInfo2()
{
}
public string Path
{
get
{
return _fi.FullName;
}
set
{
value = Path;
}
}
public long Size { get; set; }
}
Related
The scenario here is with XML, but I think it might be rather generic related.
I'm trying to deserialize certain types of object from XML files. The procedures are the same for these types, therefore I'm using generic methods. XML files are large, so I'm combining XmlReader and XmlSerializer.
Example of getting the all the objects of a certain type:
public static List<T> GetAll<T>() where T : IMyXml, new()
{
var toReturn = new List<T>();
T t = new T();
string fileName;
// different types are located in different XML files
if (t.Type == XmlTypeEnum.A) fileName = "A.xml";
else if (t.Type == XmlTypeEnum.B) fileName = "B.xml";
else throw ... ;
using (var stream = GetStreamFromResource(fileName)) //get file from embedded resource
{
using (var reader = XmlReader.Create(stream))
{
reader.ReadToFollowing(t.ParentName);
while (reader.ReadToFollowing(t.SelfName))
{
var serializer = new XmlSerializer(typeof(T));
t = (T)serializer.Deserialize(reader.ReadSubtree());
toReturn.Add(t);
}
}
}
return toReturn;
}
While all the types implement a common interface
[XmlType]
public interface IMyXml
{
[XmlIgnore]
string ParentName { get; }
[XmlIgnore]
string SelfName { get; }
[XmlIgnore]
XmlTypeEnum Type { get; }
}
The issues here:
Generic types are not supposed to be if-ed (am I right though?), I have to add an enum as property in order to achieve that
Interface cannot have static properties, I have to instantiate a dummy object to retrieve properties
So the code here looks kinda weird to me, is there a better way to do this?
I'm sending this structure through webapi:
[DataContract]
public class PacketData
{
public enum Opcodes
{
Hello = 0x00,
Close = 0x01,
Serial = 0x02,
GPIO = 0x04
}
[DataMember]
public object Data { get; private set; }
[DataMember]
public Opcodes Opcode { get; private set; }
public PacketData(Opcodes opcode, object data)
{
Data = data;
Opcode = opcode;
}
}
And my problem is that I set on server side when I sending it I assign to Data few class ex. CustomClass1, CustomClass2
Now on deserialize I get instead of object string which is:
{\r\n \"Cmd\": 5,\r\n \"BaudRates\": [\r\n 2400,\r\n 4800,\r\n 9600,\r\n 19200,\r\n 38400,\r\n 57600,\r\n 115200\r\n ],\r\n \"SerialPorts\": null,\r\n \"IsOpen\": false,\r\n \"BaudRate\": 0,\r\n \"PortName\": null,\r\n \"WriteCMD\": null,\r\n \"WriteDATA\": null,\r\n \"Read\": null\r\n}
So Data is string instead of class or C# classic object type
And there is problem I don't know how to recognize from string if its CustomClass1 or CustomClass2.
Any ideas how to resolve this?
Thanks.
EDIT: Including deserialize and serialize
[HttpGet("Send", Name = "Send")]
public IActionResult Send()
{
return Ok(WebAPI.Send(), HttpStatusCode.OK);
}
public IEnumerable<string> Send()
{
List<string> packets = new List<string>();
foreach (PacketData packet in StaticConfig.SendStack.ToArray())
packets.Add(JsonConvert.SerializeObject(packet));
return packets.ToArray();
}
And this is deserialize:
string json = await client.GetStringAsync(new Uri("http://localhost:5000/api/Send"));
string[] jsonArray = JsonConvert.DeserializeObject<string[]>(json);
if (jsonArray.Length == 0)
Task.Delay(100).Wait();
List<PacketData> packets = new List<PacketData>();
foreach (string j in jsonArray)
packets.Add(JsonConvert.DeserializeObject<PacketData>(j));
foreach (PacketData packet in packets)
{
string p = packet.Data.ToString();
bool a = packet.Data is PacketSerialModel; // returns false
HandleReceivedData(this, new ReceivedDataArgs(packet));
}
EDIT 2:
So what do I want?
I would like to get back mentioned string into PacketData.Data then I can use something like this:
if(packet.Data is CustomClass1)
{
}
else if(packet.Data is CustomClass2)
{
var b = packetData as CustomClass2;
//...
}
currently my packet.Data is string and I need to create on this object properties and set values to them based on json.
EDIT3:
using now
JsonSerializerSettings()
{ TypeNameHandling = TypeNameHandling.Auto }
Works perfectly, but I have to replace in incoming json string project name
like in following string:
["{\"Data\":{\"$type\":\"Shared.PacketSerialModel, ASP_MVC\",\"Cmd\":5,\"BaudRates\":[2400,4800,9600,19200,38400,57600,115200],\"SerialPorts\":null,\"IsOpen\":false,\"BaudRate\":0,\"PortName\":null,\"WriteCMD\":null,\"WriteDATA\":null,\"Read\":null},\"Opcode\":2}"]
I have to replace ASP_MVC to second project name, any workaround than replace?
To answer your updated question, "how do I remap assembly names when exchanging JSON data containing $type information between different .Net assemblies using Json.NET", you have a few options:
The simplest would be to extract the types in question into a shared DLL and reference that DLL from both assemblies. This solves the problem and also reduces code duplication.
If you cannot do this, you will need to write your own SerializationBinder, probably inheriting from Json.NET's DefaultSerializationBinder, as is described in the documentation: Custom SerializationBinder. If your classes are not generic, can simply remap the assemblyName:
public class SimpleAssemblyMappingSerializationBinder : DefaultSerializationBinder
{
readonly string oldAssemblyName;
readonly string newAssemblyName;
public SimpleAssemblyMappingSerializationBinder(string oldAssemblyName, string newAssemblyName)
{
this.oldAssemblyName = oldAssemblyName;
this.newAssemblyName = newAssemblyName;
}
public override Type BindToType(string assemblyName, string typeName)
{
if (assemblyName == oldAssemblyName)
assemblyName = newAssemblyName;
return base.BindToType(assemblyName, typeName);
}
}
For a similar binder, see Handling namespace changes with TypeNameHandling.All.
But if your classes are generic (for instance, if Data is sometimes a List<CustomClass3>), you will need to parse the generic parameters inside the type name. For an idea of how to do this see How to create a SerializationBinder for the Binary Formatter that handles the moving of types from one assembly and namespace to another. That question is about BinaryFormatter but the answer applies to Json.NET also.
To omit the assembly name and namespace entirely, you could use the binder from Json serialization for Object Data Type for both serialization and deserialization.
Finally, you could consider switching to DataContractJsonSerializer which exchanges type information indirectly by using contract names rather than raw .Net type names. While the data contract serializer is generally less flexible than Json.NET, its contract-based exchange of type information may be more suitable in this case. See for instance How to deserialize JSON with unnamed collection of types using DataContractSerializer.
I have a class which represent a json api. I have created a constructor that is using switched enums to select how the object is to be populated. One is for the minimum equivalent json object. Another is intended populate the properties by reading in from a file. So, I can read the file into a string and deserialize it, but what do I do next to populate the properties?
// this code is in the constructor
string text = System.IO.File.ReadAllText(fileName);
this.???? = JsonConvert.DeserializeObject<MyObject>(text); // MyObject is the object the constructor is working on
Can I cast the deserialized text into the object's properties?
Sorry for asking something that has probably already been asked, but I don't know if I am even asking the question properly (let alone searching for it). Thanks...
Not tested, but I think you could do something like this:
public MyObject(MyEnum e)
{
switch(e)
{
case MyEnum.ValueThatMeansWeDeserializeFromJSON:
string text = System.IO.File.ReadAllText(fileName);
var serializer = new JsonSerializer();
serializer.Populate(new JsonTextReader(new StringReader(text)), this);
break;
}
}
Populate will take an existing object and try to deserialize the properties from JSON into that object (as opposed to DeserializeObject which will create a new object.
As I mentioned in a comment, use a factory instead of the switch in the constructor.
If you want to keep it in the constructor use automaper and do this instead
public class MyObject
{
public MyObject()
{
}
public MyObject(Enum e)
{
string text = System.IO.File.ReadAllText(fileName);
var source = JsonConvert.DeserializeObject<MyObject>(text);
Mapper.CreateMap<MyObject, MyObject>();
Mapper.Map(source, this);
}
public string Name { get; set; }
}
While trying to de-serialize a complex JSON object (JIRA issue) into an object containing a dictionary of type string-Field I've hit a bit of a bump.
While I can de-serialize various pre-determined object types (standard), I'm having a bit of a harder time with the custom fields, which could be of various types (they all begin with customfield_ followed by a set of numbers).
The custom fields can be floats, strings, booleans, objects and arrays of objects. The latter of these is causing me issues since I can't seem to determine what the object is before I de-serialize it.
I've searched for a way to perhaps "peek" at the data in the object before de-serializing as one of the fields contains information specific to it's type. This is all so I can determine the type of the object and tell Json.Net what to de-serialize it as.
I've considered parsing the JSON string before serialization to get the information, or maybe just when hitting this particular case, but maybe there is a better way?
Thanks in advance for any advice on this.
You can deserialize to an object with Json.Net. Here's a quick and dirty example:
using System;
using Newtonsoft.Json;
namespace Sandbox
{
class Program
{
private static void Main(string[] args)
{
var nestDto = new Dto
{
customfield_1 = 20,
customfield_2 = "Test2"
};
var dto = new Dto
{
customfield_1 = 10,
customfield_3 = new[] { nestDto },
customfield_2 = "Test"
};
var jsonString = JsonConvert.SerializeObject(dto);
Console.WriteLine(jsonString);
var fromJsonString = JsonConvert.DeserializeObject<Dto>(jsonString);
Console.WriteLine(fromJsonString.customfield_3[0].customfield_2); //Outputs Test2
Console.ReadKey();
}
}
class Dto
{
public int customfield_1 { get; set; }
public string customfield_2 { get; set; }
public Dto[] customfield_3 { get; set; }
}
}
Instead of peaking, you can deserialize as the same type as JSON.net uses for ExtensionData explicitly. For example:
if (reader.TokenType == JsonToken.StartArray)
{
var values = serializer.Deserialize<List<Dictionary<string, JToken>>>(reader);
objectContainer = ClassifyAndReturn(values);
}
private ObjectType ClassifyAndReturn(List<Dictionary<string, JToken>> values)
{
if (values.First().ContainsKey("self"))
{
string self = values.First()["self"].Value<string>();
if (self.Contains("customFieldOption"))
//... Then go into a series of if else cases to determine the object.
The representation of the objects are given as a Dictionary of string to JToken, which can then easily be checked and assigned manually or in some cases automatically deserialized (in the case one of the fields is another object).
Here is what an object constructor could look like:
internal myobject(Dictionary<string, JToken> source)
{
Self = source["self"].Value<string>();
Id = source["id"].Value<string>();
Value = source["value"].Value<string>();
}
I have an object I want to deserialize, which is declared as struct in an external library (which is marked as Serializable).However, Json.net is not able to deserialize this object. Serialization is fine, but deserialize doesn't work. I already tried to change struct to class but it didn't help. Do I need to put something special in the JsonSerializerSettings or something else, or isn't it supported? I already tried several of the parameters there, like TypeNameHandling and TypeNameAssemblyFormat, but it didn't work.
As asked, some code, this is declared in external library:
[Serializable]
public struct BarEntry
{
public RegistryValueKind One;
public string Two;
public string Three;
public string Four;
public object Five;
}
[Serializable]
public struct FooEntry
{
public string One;
public string Two;
}
Serialized in own project as:
Stream stream = System.IO.File.Open(fileName, FileMode.Create);
string json = JsonConvert.SerializeObject(entry, Formatting.None, new JsonSerializerSettings { PreserveReferencesHandling = PreserveReferencesHandling.Objects });
StreamWriter streamWriter = new StreamWriter(stream);
streamWriter.Write(json);
streamWriter.Flush();
stream.Close();
Deserialized in own project as:
StreamReader streamReader = new StreamReader(input);
string json = streamReader.ReadToEnd();
object returnObject = JsonConvert.DeserializeObject<T>(json);
input.Close();
return returnObject;
Everything inside own project works fine with this code, but from external library it is not able to deserialize the objects (From both BarEntry and FooEntry).
Also BarEntry and FooEntry are both stored in the same property in their storage object:
public object Entry { get; set; }
You must send exact type to JsonConvert.DeserializeObject, otherwise there is no way to create right object that will have needed properties to deserialize to from json string. So load this type from external assembly and send it to Json.NET. Something like this :
Assembly assembly = Assembly.LoadFile("ExternalAssembly.dll");
Type barEntryType = assembly.GetType("BarEntry");
var returnObject = JsonConvert.DeserializeObject(json, barEntryType);
or reference the external assembly and simply do this :
BarEntry returnObject = JsonConvert.DeserializeObject<BarEntry>(json)