Why Are Some Members Missing When Trying to Print an Object by Serializing to JSON

Why are some members missing when trying to print an object by serializing to JSON?

By default Json.NET will only serialize public properties and fields. Your fields A and b are private. To cause nonpublic (private or internal) members to be serialized by Json.NET, you can:

  1. Make them public:

    public int A;
    public int b;

    However, stylistically, if you are going to make them be public, it's better to convert them to properties:

    public class Test
    {
    public int A { get; private set; }
    public int b { get; private set; }
    public Test(int a, int b)
    {
    this.A = a;
    this.b = b;
    }
    };

    Only the getters need be public for Json.NET to serialize them.

  2. Mark then with [JsonProperty]:

    [JsonProperty]
    int A;
    [JsonProperty]
    int b;

    This works for nonpublic properties as well.

  3. Mark your object with [DataContract] and your fields with [DataMember]:

    [DataContract]
    public class Test
    {
    [DataMember]
    int A;
    [DataMember]
    int b;
    public Test(int _a, int _b)
    {
    A = _a;
    b = _b;
    }
    };

    Note that data contract serialization is opt-in so you will need to mark every member to be serialized with [DataMember]. Kind of a nuisance but useful if you don't want your c# models to have a dependency on Json.NET.

    This also works for nonpublic properties.

  4. Mark your object with [JsonObject(MemberSerialization = MemberSerialization.Fields)]:

    [JsonObject(MemberSerialization = MemberSerialization.Fields)]
    public class Test
    {
    // Remainder as before...
    };

    As explained in the documentation for MemberSerialization, MemberSerialization.Fields ensures that

    All public and private fields are serialized. Members can be excluded using JsonIgnoreAttribute or NonSerializedAttribute. This member serialization mode can also be set by marking the class with SerializableAttribute and setting IgnoreSerializableAttribute on DefaultContractResolver to false.

    Of course this only causes nonpublic fields to be serialized, not nonpublic properties, but this may be what you want if your purpose is to print an arbitrary variable for debugging purposes.

  5. Use custom contract resolver that serializes all public and nonpublic fields.

    Several are shown at JSON.Net: Force serialization of all private fields and all fields in sub-classes which serialize both properties and fields that are public or private.

    Another, DeclaredFieldContractResolver from C# Serialize with JSON.NET inherited private fields, serializes only fields that are public or private by automatically assuming all objects are marked with MemberSerialization.Fields. You would use it like this:

    var settings = new JsonSerializerSettings
    {
    ContractResolver = DeclaredFieldContractResolver.Instance
    };
    var json = JsonConvert.SerializeObject(t1, Formatting.Indented, settings);
  6. Create a custom JsonConverter that serializes the necessary fields and properties. Since the fields are private it would need to be a nested type:

    public class Test
    {
    public class TestJsonConverter : JsonConverter
    {
    public override bool CanConvert(Type objectType)
    {
    return typeof(Test).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
    var test = (Test)value;

    writer.WriteStartObject();
    writer.WritePropertyName(nameof(Test.A));
    serializer.Serialize(writer, test.A);
    writer.WritePropertyName(nameof(Test.b));
    serializer.Serialize(writer, test.b);
    writer.WriteEndObject();
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
    throw new NotImplementedException();
    }
    }
    // Remainder as before
    }

    And use it like:

    var json = JsonConvert.SerializeObject(t1, Formatting.Indented, new Test.TestJsonConverter());

    While this works, since the type is nested your model will still have a dependency on Json.NET, which makes option #2 the better choice.

If you are only dumping your object for debugging purposes, #5 might be the best option.

C# Object Serialized using Newton.JSON is Empty

Make your fields public, because default NewtonSoft.Json will only serialize public members or
for some reason you really don't want to make your fields public, you can use the JsonPropertyAttribute to allow them to be serialize and deserialize.

JSON Serializer object with internal properties

Mark the internal properties to be serialized with the [JsonProperty] attribute:

public class Foo
{
[JsonProperty]
internal int num1 { get; set; }
[JsonProperty]
internal double num2 { get; set; }

public string Description { get; set; }

public override string ToString()
{
if (!string.IsNullOrEmpty(Description))
return Description;

return base.ToString();
}
}

And then later, to test:

Foo f = new Foo();
f.Description = "Foo Example";
f.num1 = 101;
f.num2 = 202;
JsonSerializerSettings settings = new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.All };

var jsonOutput = JsonConvert.SerializeObject(f, Formatting.Indented, settings);

Console.WriteLine(jsonOutput);

I get the following output:

{
"$type": "Tile.JsonInternalPropertySerialization.Foo, Tile",
"num1": 101,
"num2": 202.0,
"Description": "Foo Example"
}

(Where "Tile.JsonInternalPropertySerialization" and "Tile" are namespace and assembly names I am using).

As an aside, when using TypeNameHandling, do take note of this caution from the Newtonsoft docs:

TypeNameHandling should be used with caution when your application deserializes JSON from an external source. Incoming types should be validated with a custom SerializationBinder when deserializing with a value other than None.

For a discussion of why this may be necessary, see TypeNameHandling caution in Newtonsoft Json and and External json vulnerable because of Json.Net TypeNameHandling auto?.

Newtonsoft.JSON serializeobject returns empty JSON string

It appears that Xamarin Live Player has some issues with the serializer. I tried plugging my phone via USB and it works!

Detect if deserialized object is missing a field with the JsonConvert class in Json.NET

The Json.Net serializer has a MissingMemberHandling setting which you can set to Error. (The default is Ignore.) This will cause the serializer to throw a JsonSerializationException during deserialization whenever it encounters a JSON property for which there is no corresponding property in the target class.

static void Main(string[] args)
{
try
{
JsonSerializerSettings settings = new JsonSerializerSettings();
settings.MissingMemberHandling = MissingMemberHandling.Error;

var goodObj = JsonConvert.DeserializeObject<MyJsonObjView>(correctData, settings);
System.Console.Out.WriteLine(goodObj.MyJsonInt.ToString());

var badObj = JsonConvert.DeserializeObject<MyJsonObjView>(wrongData, settings);
System.Console.Out.WriteLine(badObj.MyJsonInt.ToString());
}
catch (Exception ex)
{
Console.WriteLine(ex.GetType().Name + ": " + ex.Message);
}
}

Result:

42
JsonSerializationException: Could not find member 'SomeOtherProperty' on object
of type 'MyJsonObjView'. Path 'SomeOtherProperty', line 3, position 33.

See: MissingMemberHandling setting.

How to make a class JSON serializable

Do you have an idea about the expected output? For example, will this do?

>>> f  = FileItem("/foo/bar")
>>> magic(f)
'{"fname": "/foo/bar"}'

In that case you can merely call json.dumps(f.__dict__).

If you want more customized output then you will have to subclass JSONEncoder and implement your own custom serialization.

For a trivial example, see below.

>>> from json import JSONEncoder
>>> class MyEncoder(JSONEncoder):
def default(self, o):
return o.__dict__

>>> MyEncoder().encode(f)
'{"fname": "/foo/bar"}'

Then you pass this class into the json.dumps() method as cls kwarg:

json.dumps(cls=MyEncoder)

If you also want to decode then you'll have to supply a custom object_hook to the JSONDecoder class. For example:

>>> def from_json(json_object):
if 'fname' in json_object:
return FileItem(json_object['fname'])
>>> f = JSONDecoder(object_hook = from_json).decode('{"fname": "/foo/bar"}')
>>> f
<__main__.FileItem object at 0x9337fac>
>>>

Why isn't a member of type Option[String] being serialized?

The nextId field is serializable, otherwise, you wouldn't have been able to write a Foo to JSON at all.

jsonFoo: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"}}

The problem you're having is that there are no Reads for Option[A]. Options are handled specially by Play JSON. When using JSON combinators, we use readNullable[A] and writeNullable[A] instead of read[Option[A]] and write[Option[A]]. Likewise, when using methods to pull individual fields from a JsValue, calling as will not work because it requires an implicit Reads[A] for the type you give it (in this case a Reads[Option[String]], which does not exist).

Instead, you need to use asOpt, which will correctly handle the Option underneath:

scala> (jsonFoo \ "nextId").asOpt[String]
res1: Option[String] = None

nextId doesn't appear in the printed JSON because the value you are serializing is None. This is what is expected to happen. Since the value is optional, it gets omitted from the JSON (it's just undefined in JavaScript). If it has a value, it will appear:

scala> Json.toJson(Foo("id", FooType("a", "b"), Option("abcdef")))
res3: play.api.libs.json.JsValue = {"id":"id","fooType":{"a":"a","b":"b"},"nextId":"abcdef"}

If something wasn't serializable with Play JSON, it simply wouldn't compile.



Related Topics



Leave a reply



Submit