Silverlight 4 DataContractJsonSerializer, private fields of a derived class - json

I use DataContractJsonSerializer to deserialize json data in Silverlight 4.
Json data key names do not match my class property names; so I guess I have to use
DataMemberAttribute. So I did the following:
[DataContract]
public class Person : Model
{
[DataMember(Name = "id")]
private int _id;
public int Id
{
get { return _id; }
set { _id = value; }
}
[DataMember(Name = "name")]
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
}
Now deserialization fails because I didn't apply DataContractAttribute to Person's base class Model. Is it a strict requirement? Also, after I applied DataContractAttribute to Model, deserialization fails again, because I applied DataMember attributes to private fields, not to the public properties. Why can't I apply them to private members (the documentation seems to say otherwise).
NOTE: server-side code is not ASP.NET; so WCF isn't used.

In order to get the private members to serialize over WCF correctly, we had to change them all to protected internal instead of private. Maybe the same applies for DataContractJsonSerializer?

Related

spring boot json to model conversion error

{
"cust":"A",
"del":[{
"type1": "id",
"type2":[{
"name":"address"
}]
}]
I have converted this json to below model class
public class Del{
public String type1;
public JSONArray type2; // "type2" has dynamic key and value which are string, it can have "name":"address","id":"sal" pair etc dynamically
}
public class Root{
public String cust;
public List<Del> del;
}
But, this mapping is showing error. how to fetch key and value inside "type2" in dynamic way where attribute names are not fixed.
If you have a list, you need a List<T>. What is T? If it can have any number of keys, then it must be a Map<K, V>. K is String, and V is either String or Object, depending on your actual structure. If you have a handful of expected keys and want to ignore everything else, make T a custom class of yours:
class Type2Props {
public String name;
public String address;
public String id;
public String sal;
}
Properties/fields without a value in the JSON will be left initialized as null.
Putting it together, you either need
public class Del {
public String type1;
public List<Type2Props> type2;
}
or
public class Del {
public String type1;
public List<Map<String, String>> type2; /* might be `Map<String, Object>` */
}

How to deal with nullable reference types with System.Text.Json?

I have upgraded my project to netcore 3.0 and I am in the middle of refactoring a project to use the new nullable references types feature, but got stuck pretty quickly because of the following issue.
Lets say I consume a REST api which returns the following JSON:
{
"Name": "Volvo 240",
"Year": 1989
}
This api always returns the name/year, so they are non-nullable.
I would use this simple class for deserialization:
public class Car
{
public string Name {get; set;}
public int Year {get; set;}
}
And I would deserialize this to a Car instance using the new System.Text.Json
var car = JsonSerializer.Deserialize<Car>(json);
This all works, but when enabling nullable reference types I get a warning in the Car class that Name is declared as non-nullable but can be null. I understand why I get this since it is possible to instantiate this object without initializing the Name property.
So ideally Car should look like this:
public class Car
{
public string Name { get; }
public int Year { get; }
public Car(string name, int year)
{
Name = name;
Year = year;
}
}
But this doesn't work because System.Text.Json serializer doesn't support constructors with parameters.
So my question is: How would I declare Car so that Name is non-nullable and get it to work with System.Text.Json without getting "non-nullable" warning?`
I don't want to make it nullable because I would have to do null-checks on basically everything when enabling nullable reference types, and since the REST API in my example says that they are always provided they shouldn't be nullable.
UPDATE
System.Text.Json for .NET 5 now supports parameterized constructors, so this should not be a problem anymore.
See https://learn.microsoft.com/en-us/dotnet/standard/serialization/system-text-json-immutability?pivots=dotnet-5-0
Old answer below
After reading the msdocs I found out how I could solve this issue.
So until System.Text.Json cannot instantiate classes with parameters in their constructor, the Car class will have to look like this:
public class Car
{
public string Name { get; set; } = default!;
public int Year { get; set; }
}
Update
If you're on net5, use the parameterized constructor support now offer as #langen points out. Else below can still be useful.
Original
Slightly alternative approach. System.Text.Json appears to have no problems using a private parameterless constructor. So you can at least do the following:
public class Car
{
public string Name { get; set; }
public int Year { get; set; }
// For System.Text.Json deserialization only
#pragma warning disable CS8618 // Non-nullable field is uninitialized.
private Car() { }
#pragma warning restore CS8618
public Car(string name, int year)
{
Name = name
?? throw new ArgumentNullException(nameof(name));
Year = year;
}
}
Benefits being:
Init of the object from your own code must be through the public ctor.
You don't need to do = null!; on each property.
Remaining downside with S.T.Json and nullable reference types:
S.T.Json still requires setters on the properties to actually set the values during deserialization. I tried with private ones and it's a no go, so we still can't get an immutable object...
Another option, for those who want to handle missing properties with meaningful exceptions:
using System;
public class Car
{
private string? name;
private int? year;
public string Name
{
get => this.name ?? throw new InvalidOperationException($"{nameof(this.Name)} was not set.");
set => this.name = value;
}
public int Year
{
get => this.year ?? throw new InvalidOperationException($"{nameof(this.Year)} was not set.");
set => this.year = value;
}
}

How to combine #JsonView with #JsonProperty?

I have a DTO class that should serve json via a spring-mvc #RestController.
I want to provide different version/views on the same object. Especially, there are fields that are only used in VERSION_1 of the api, and some only in VERSION_2.
Problem: I could add #JsonView for this, but my goal is also to rename those fields. Some fields should actually replace the same name from previous versions.
Example:
public class Person {
#JsonView(View.Version_1.class)
#JsonProperty("name")
private String name; //eg only the firstname
#JsonView(View.Version_2.class)
#JsonProperty("name")
private NameDTO namedto; //now changing to first+last name
static class NameDTO {
private String firstname;
private String lastname;
}
}
#RestController
public class MyServlet {
#GetMapping("/person/{id}")
#JsonView(View.Version_1.class)
public PersonDTO person1(int id) {
//...
}
#GetMapping("/person_new/{id}")
#JsonView(View.Version_2.class)
public PersonDTO person2(int id) {
//...
}
}
So, depending on the view/version, you would get the same json field firstname, but with different content.
In this example, using V1 would give:
{"name": "john"}
Whereas using V2 should result in:
{"name": {"firstname": "john", "lastname": "doe"}}
BUT not with he code above, as jackson complains:
com.fasterxml.jackson.databind.JsonMappingException: Conflicting
getter definitions for property "name".
Is that possible at all?
I found a way using:
https://github.com/jonpeterson/spring-webmvc-model-versioning
Basic idea is to add a custom VersionedModelConverter that is applied on #VersionedModelConverter annotated webservice response classes.
#Configuration
#Import(VersionedModelResponseBodyAdvice.class)
public class SpringMvcVersioningConfiguration {
//register in jackson. spring-boot automatically registers any module beans
#Bean
public Model versioningModel() {
return new VersioningModule();
}
}
#GetMapping
#VersionedResponseBody(defaultVersion = "2.0")
public Person person() {
}
#JsonVersionedModel(currentVersion = "3.0" toPastConverterClass = PersonConverter.class)
public class Person {
}
public class PersonConverter implements VersionedModelConverter {
#Override
public ObjectNode convert(ObjectNode modelData, String modelVersion, String targetModelVersion, JsonNodeFactory nodeFactory) {
Double modelv = Double.valueOf(modelVersion);
Double targetv = Double.valueOf(targetVersion);
//todo if-else based on model version
Object node = modelData.remove("fieldname");
//node.change...
modelData.set("fieldname_renamed", node);
}
}

How to omit Get only properties in servicestack json serializer?

I have an object which I am de-serializing using ToJson<>() method from ServiceStack.Text namespace.
How to omit all the GET only propeties during serialization? Is there any attribute like [Ignore] or something that I can decorate my properties with, so that they can be omitted?
Thanks
ServiceStack's Text serializers follows .NET's DataContract serializer behavior, which means you can ignore data members by using the opt-out [IgnoreDataMember] attribute
public class Poco
{
public int Id { get; set; }
public string Name { get; set; }
[IgnoreDataMember]
public string IsIgnored { get; set; }
}
An opt-in alternative is to decorate every property you want serialized with [DataMember]. The remaining properties aren't serialized, e.g:
[DataContract]
public class Poco
{
[DataMember]
public int Id { get; set; }
[DataMember]
public string Name { get; set; }
public string IsIgnored { get; set; }
}
Finally there's also a non-intrusive option that doesn't require attributes, e.g:
JsConfig<Poco>.ExcludePropertyNames = new [] { "IsIgnored" };
Dynamically specifying properties that should be serialized
ServiceStack's Serializers also supports dynamically controlling serialization by providing conventionally named ShouldSerialize({PropertyName}) methods to indicate whether a property should be serialized or not, e.g:
public class Poco
{
public int Id { get; set; }
public string Name { get; set; }
public string IsIgnored { get; set; }
public bool? ShouldSerialize(string fieldName)
{
return fieldName == "IsIgnored";
}
}
More examples in ConditionalSerializationTests.cs
For nullable members, you also have the ability to set it to null before serializing.
This is particularly useful if you want to create a single view/api model that is re-used for several API calls. The service can touch it up before setting it on the response object.
Example:
public SignInPostResponse Post(SignInPost request)
{
UserAuthentication auth = _userService.SignIn(request.Domain, true, request.Username, request.Password);
// Map domain model ojbect to API model object. These classes are used with several API calls.
var webAuth = Map<WebUserAuthentication>(auth);
// Exmaple: Clear a property that I don't want to return for this API call... for whatever reason.
webAuth.AuthenticationType = null;
var response = new SignInPostResponse { Results = webAuth };
return response;
}
I do wish there was a way to dynamically control the serialization of all members (including non-nullable) on a per endpoint fashion.

Jackson with JAXB - abstract types instantiation

I'm facing problem with Jackson's ObjectMapper using JAXB annotations. To be concrete, I'm having collection with interface generic information and although I can deserialize input from XML, it is not possible with Jackson (using JAXB introspector). Maybe I'm just missing some configuration property or JAXB annotation? The problem is that "abstract types can only be instantiated with additional type information", I thought #XmlElementRef (or #XmlElement) with type information will handle this problem, but obviosly it does not.
Please note, that I want to stay only with JAXB annotations if possible.
E.g. using #JsonTypeInfo or #JsonDeserialize would be the last thing to do.
IEntry.java:
#XmlSeeAlso(Entry.class)
public interface IEntry {
String getValue();
}
Entry.java:
#XmlRootElement(name = "entry")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
Aggregator.java:
#XmlRootElement(name = "aggregator")
public class Aggregator {
#XmlElementRef(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
Test method:
#Test
public void testSerialization() throws Exception {
ObjectMapper om = new ObjectMapper();
AnnotationIntrospector intr = new JaxbAnnotationIntrospector();
om.getDeserializationConfig().withAnnotationIntrospector(intr);
String json = "{\"entries\":[{\"value\":\"X\"},{\"value\":\"Y\"},{\"value\":\"Z\"}]}\";\n}";
Aggregator agr = om.readValue(json, Aggregator.class);
}
Thanks for all response
Note: I'm the EclipseLink JAXB (MOXy) lead and a member of the JAXB (JSR-222) expert group.
I am not sure if Jackson supports this use case or not, but you appear to be using #XmlElementRef incorrectly. When you use #XmlElementRef the root element name associated with the class is used to determine the instance to be instantiated. If your example the node entries does not match the #XmlRootElement(name="entry") annotation.
You could try one of the following options (they all work with MOXy's JSON binding, see: http://blog.bdoughan.com/2011/08/json-binding-with-eclipselink-moxy.html):
OPTION 1 - Change #XMLRootElement on Entry
#XmlRootElement(name = "entries")
public class Entry implements IEntry {
#XmlElement(name = "value")
String value;
public Entry() {
}
public Entry(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
OPTION #2 - Change the JSON Document
{"entry":[{"value":"X"},{"value":"Y"},{"value":"Z"}]}}
OPTION #3 - Use #XMLElement instead of #XMLElementRef
If you use the #XmlElement annotation you can specify on the field/property what the node name should be instead of relying on the #XmlRootElement annotation. Also if you annotate the fields you should specify #XmlAccessorType(XmlAccessType.FIELD) at the type level.
import java.util.Set;
import javax.xml.bind.annotation.*;
#XmlRootElement(name = "aggregator")
#XmlAccessorType(XmlAccessType.FIELD)
public class Aggregator {
#XmlElement(type = Entry.class)
private Set<IEntry> entries;
public Aggregator() {
}
public Aggregator(Set<IEntry> entries) {
this.entries = entries;
}
public Set<IEntry> getEntries() {
return entries;
}
}
For More Information
http://blog.bdoughan.com/2010/11/jaxb-and-inheritance-using-substitution.html
http://blog.bdoughan.com/2011/05/jaxb-and-interface-fronted-models.html
import org.codehaus.jackson.map.annotate.JsonDeserialize;
#JsonDeserialize(as = Entry.class)
public interface IEntry {
String getValue();
}