I have written a custom Jackson JsonSerializer to serialise my Money POJOs to something like:
{
"amountMinorUnits": 123,
"currency": "USD",
}
However in the Swagger when a Response POJO references a Money pojo, the Swagger sees the original Money POJO and makes out the API uses that, and not the customer serialised version.
so if I have
class Person {
Money salary;
...
}
How do I make clear what the Money JSON is?
Thanks
Use directModelSubstitute in your swagger config.
Example for spring boot app. just focus on swagger configuration:
#Configuration
#EnableSwagger2
public class SwaggerConfig {
#Bean
public Docket api() {
return new Docket(DocumentationType.SWAGGER_2)
.directModelSubstitute(Response.class, ResponseSerializer.class)
.select()
.apis(RequestHandlerSelectors.any())
.paths(PathSelectors.any())
.build();
}
Related
Often i heard that "try to avoid if/switch constructions. If you have them then refactor them to subclasses"
I don't realize how this thing works.
Ok, you have a if/switch in your code. And you create several new classes. But to decide which class you will use you need to implement switch if in fabric class (where you generate these objects). Am i wrong?
P.S. Sorry for my English. I'm reader, not writer.
But to decide which class you will use you need to implement switch if
in fabric class (where you generate these objects). Am i wrong?
No, you are not wrong. While the Polymorphism over switches is a good thing, there are exceptions. One such exception is when you have parameterized factory, and that's absolutely acceptable. So instead of your client code creating specialized classes based on conditions, you will ask such factory to create them for you. Advantage is Factory will solely be responsible for creating those class instances, and if new class is introduced only factory will be modified not client code.
So instead of this:
public class Client {
public string Serialize<T>(string contentType, T instance) where T : class {
switch(contentType) {
case "XML":
return new XMLSerializer().Serialize(instance);
case "JSON":
return new JSONSerializer().Serialize(instance);
}
}
}
You will have this:
public interface ISerializer {
string Serialize(object instance);
object Deserialize(string content);
}
public class XMLSerializer : ISerializer { }
public class JSONSerializer : ISerializer { }
public class SerializerFactory() {
public static ISerializer CreateSerializer(string type) {
switch(type) {
case "XML":
return new XMLSerializer();
case "JSON":
return new JSONSerializer();
}
}
}
public class Client {
public string ParseAPIResponse(string contentType, string responseData) {
ISerializer serializer = SerializerFactory.CreateSerializer(contentType);
var responseObj = serializer.Deserialize(responseData);
}
}
Note there can be only one reason for Factory to change and that is introduction of new Serializer, so we are good on SRP here. Going even further there are ways by which you can avoid modifying factory too, using config files to store identifier-type mappings or simply exposing another method on factory to allow it's users to register new types etc. That's on you.
I am writing a Spring Boot app (RESTful webservice) that uses Jackson for serialization. I have the following data models that will be sent back and forth between the service and its HTTP clients (hence these will be serialized/deserialized to/from JSON):
public abstract class BaseEntity {
#JsonIgnore
private Long id;
private UUID refId;
// Getters, setters, ctors, etc.
}
public abstract class BaseLookup extends BaseEntity {
private String name;
private String label;
private String description;
// Getters, setters, ctors, etc.
}
public class State extends BaseLookup {
private String abbrev; // "VT", "FL", etc.
// Getters, setters, ctors, etc.
}
public class Contact extends BaseEntity {
private String givenName;
private String surname;
private State state;
// Getters, setters, ctors, etc.
}
public class Account extends BaseEntity {
private Contact contact;
private String code;
// lots of other fields that will be generated server-side
// Getters, setters, ctors, etc.
}
Thus there will be some endpoints for CRUDding Accounts, others for CRUDding Contacts, etc. For instance, the AccountController will expose endpoints for CRUDding Account instances:
#RestController
#RequestMapping(value = "/accounts")
public class AccountController {
#RequestMapping(method = RequestMethod.POST)
public void createAccount(#RequestBody Account account) {
// Do stuff and persist the account to the DB
}
}
I want to simplify the JSON that HTTP clients must craft in order to create new Account, Contact, etc. instances. At the same time there are fields on those data models that I do not want exposed to the client-side. Things like the BaseEntity#id (which is the PK of the entity in the DB). Or for instance, in the case of State, I just want the client-side to know about (and use) the abbrev field, etc. I don't want them to ever see the other BaseLookup fields or even know about them.
Hence, my end goal is to allow the client to POST the following JSON, and have a custom Jackson deserializer convert that JSON into an Account instance:
{
"contact" : {
"givenName" : "Him",
"surname" : "Himself",
"state" : "NY"
},
"code" : "12345"
}
So you see, like I stated above, this JSON accomplishes several things:
The client-side doesn't provide a BaseEntity#id or BaseEntity#refId when POSTing to create a new instance
For the contact.state field, which is a BaseLookup with several fields (id, refId, name, label, description, abbrev), the user only has to provide the abbrev field, and the deserializer is expected to figure out which State the client is referring to
The Account class actually has many other fields that are inferred/generated server-side; the client doesn't need to know about them in order to create an Account instance
The JSON above is a simplified form of what we would get if we serialized an Account with Jackson's default behavior; this is to make things easier on the client-side and even more secure on the server-side (not exposing PKs, etc.)
The important thing to note here is that the JSON sent to this controller for the contact field is identical to the JSON that will be POSTed to a ContactController for creating new Contact instances.
Here's the problem:
public class AccountDeserializer extends StdDeserializer<Account> {
public AccountDeserializer() {
this(null);
}
public AccountDeserializer(Class<Account> accClazz) {
super(accClazz);
}
#Override
public Account deserialize(JsonParser jsonParser, DeserializationContext dCtx)
throws IOException, JsonProcessingException {
JsonNode jsonNode = jsonParser.codec.readTree(jsonParser)
Contact contact = ??? // TODO: How to invoke ContactDeserializer here?
String accountCode = node.get("code").asText();
// Generate lots of other Account field values here...
Account account = new Account(contact, accountCode, /* other fields here */);
return account;
}
}
Since I will also have a ContactController (for CRUDding Contact instances irrespective of an associated Account), and because I have similar desires to hide Contact fields from the client-side as well as to simplify the JSON coming into this ContactController#createContact endpoint, I will also need a ContactDeserializer in addition to this AccountDeserializer...
public class ContactDeserializer extends StdDeserializer<Contact> {
// ...etc.
}
This ContactDeserializer will be responsible for converting JSON into Contact instances. But since Account instances also contain Contact instances, and because the "contact JSON" inside the outer "account JSON" will be the same as any JSON that the client sends to any of the "contact endpoints", I'd like to invoke the ContactDeserializer from inside the AccountDeserializer somehow.
That way, when the ContactController receives "contact JSON" to create a new Contact instance, the ContactDeserializer is engaged to get the job done. And, if the AccountController receives "account JSON" to create a new Account instance, then the AccountDeserializer is engaged to get that job done...and it uses the ContactDeserialzer to handle the deserialization of the account JSON's internal contact field as well.
Can this be done?! Can one Jackson deserializer reuse other deserializers inside of it? If so, how? If not, then what's the solution here?!
You can invoke ContactDeserializer by calling the treeToValue method of ObjectCodec. Jackson will automatically pick up the ContactDeserializer for you if you've registered it on your ObjectMapper.
public class AccountDeserializer extends JsonDeserializer<Account> {
#Override
public Account deserialize(JsonParser p, DeserializationContext ctx) throws IOException {
JsonNode node = p.readValueAsTree();
JsonNode contactNode = node.get("contact");
Contact contact = null;
if (contactNode != null) {
contact = p.getCodec().treeToValue(contactNode, Contact.class);
}
return new Account(contact, /* account properties */);
}
}
Edit
If you want to add your deserializers to existing mapper which is created by Spring Boot, you can autowire it in one of your configuration classes and customize as you like.
#Configuration
public class ObjectMapperConfiguration {
#Autowired
public void configureObjectMapper(ObjectMapper mapper) {
SimpleModule module = new SimpleModule()
.addDeserializer(Account.class, new AccountDeserializer())
.addDeserializer(Contact.class, new ContactDeserializer());
mapper.registerModule(module);
}
}
Assuming I have this objects:
class Person {
String name;
Household getHousehold();
}
class Household {
Set<Address> getAddresses();
String householdId;
}
which would normally be serialized as follows
{
"name": "XXX",
"household": {
"addresses": [...]
}
}
Is there a way to configure Jackson with annotations / mix-ins to obtain this (ie. without using DTO) ?
{
"name": "XXX",
"addresses": [...],
"household": {
"householdId": 123
}
}
You can configure the unwrapping of a specific property by both using mixins and annotations:
1. Mixins
Assuming you define the following mixin:
public abstract class UnwrappedAddresses {
#JsonUnwrapped
public abstract Household getHouseHold();
}
And then add a custom module to your objectMapper which applies the mixin to the Person class as follows:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper .registerModule(new SimpleModule() {
#Override
public void setupModule(SetupContext context) {
context.setMixInAnnotations(Person.class, UnwrappedAddresses.class);
}
});
This approach does not change the Household serialization as a single item, but just unwraps a household item when it's encapsulated in a Person object.
2. Annotations
Just add #JsonUnwrapped to your getHouseHold() method.
EDIT: After post changes.
What you want is basically to change the output of the json, which can be done by using the #JsonAnyGetter annotation(which can dynamically add new properties to your pojo).
Your expected result can be achieved by ignoring the household property and unwrapping it with the help of the #JsonAnyGetter.
#JsonIgnoreProperties("houseHold")
public static class Person {
String name;
Household houseHold;
#JsonAnyGetter
public Map<String,Object> properties(){
Map<String,Object> additionalProps=new HashMap<>();
additionalProps.put("addresses", new ArrayList<>(houseHold.getAddresses()));
Map<String,Object> houseHolProps=new HashMap<>();
houseHolProps.put("houseHoldId", houseHold.id);
additionalProps.put("houseHold", houseHolProps);
return additionalProps;
}
..getters&setters omitted
}
Which would after serialization return
{"name":"name",
"houseHold":{"houseHoldId":0},
"addresses":[
{"houseNo":2,"street":"abc"},
{"houseNo":1,"street":"str"}
]
}
I have object like below
public class MyObject
{
private IEnumerable _data;
public MyObject(IEnumerable<int> data)
{
_data = data;
}
public IEnumerable GetData()
{
return this._data;
}
}
the _data property is private. I am storing this object in session as below using jsonserializer
var val = new MyObject(new int[] {1,2,3})
HttpContext.Session.SetString("MyKey", JsonConvert.SerializeObject(val));
then im trying to retrieve it as below
var val = HttpContext.Session.GetString("MyKey");
var myObject = JsonConvert.DeserializeObject<MyObject>(val);
However MyObject.GetData() returns null. I am assuming since _data property is private JsonSerializer is not able to serialize it.
So what are my options here to store an object in session which has private properties?
You can try to make your class serializable like this
[Serializable]
public class MyObject : ISerializable
{
private IEnumerable _data;
public MyObject(IEnumerable<int> data)
{
_data = data;
}
public IEnumerable GetData()
{
return this._data;
}
protected MyObject(SerializationInfo info, StreamingContext context)
{
_data = (IEnumerable<int>)info.GetValue(nameof(_data), typeof(IEnumerable<int>));
}
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
if (info == null)
throw new ArgumentNullException(nameof(info));
info.AddValue(nameof(_data), _data, typeof(IEnumerable<int>));
}
}
For this to work, you have to target a minimum of netstandard1.3 (.NET Framework 4.6 or .NET Core). In your project.json you need to add System.Runtime.Serialization.Formatters dependency
"netstandard1.3": {
"imports": [ "dnxcore50", "portable-net45+win8" ],
"dependencies": {
"NETStandard.Library": "1.6.0",
"System.Runtime.Serialization.Formatters": "4.0.0-rc3-24212-01"
}
}
I didn't test it on .NET Core, so no idea if it works. But this is how you implement but JSON.NET actually does support serializing ISerializable objects. I just don't know if the .NET Core version of JSON.NET does support it, because the nuget package targets netstandard1.0 and ISerializable requires System.Runtime.Serialization.Formatters which requires netstandard1.3.
I believe this is a standard JSON.Net serialization problem. Private properties aren't serialized unless you provide your own serializer as provided in this answer.
Additionally, you'll need a default constructor, or ensure to tag your constructor correctly (unsure whether that'll work with private fields). This is a good answer to the non-default constructor problem.
I feel like your class is a simple DTO without too much value of keeping this field private. Why not marking it as public? Or if your object is more complex than the example given, you might want to have a dedicated DTO with only public properties and no functionality which you create just for serialization/deserialization and then manually convert into the class you actually want. This way, you can also ensure your DTO stays compatible as you release new versions even if you introduce breaking changes in the other class.
I use Gucie 3.0 to intercept any methods that have my defined annotation #LogRequired. However for my application, some beans are initialized by Spring with injected fields values. After calling giuce injector.injectMembers(this), the beans gets proxied by guice but all original fields values are gone. Looks like Guice re-constucts the beans and throw away all old values. Is this expected behavior or how can I solve this issue?
Create a class extends AbstractModule
public class InterceptorModule extends AbstractModule{ public void configure()
{ LogInterceptor tracing = new LogInterceptor(); requestInjection(tracing); bindInterceptor(Matchers.any(), Matchers.annotatedWith(LogRequired.class), tracing); }
}
Define the interceptor business logic
public class LogInterceptor implements MethodInterceptor { //business logic here }
Create LogService class
Public class LogService { Injector injector = Guice.createInjector(new InterceptorModule()); }
I have one of the bean example below with the getName method wants to be intercepted:
public class UserImplTwo implements IUser {
private String name;
#LogRequired
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
}
which is initialized by Spring context:
Finally I have a consumer to consume the bean:
public class Consumer
{
#Inject
private UserImplTwo instance;
public void setInstance(UserImplTwo instance)
{
this.instance = instance;
}
public void init()
{
// the value of name is printed out as 'hello world'
System.out.println( this.instance.getName());
LogService.injector.injectMembers(this);
// the value of name is printed out as null, should be 'hello world'
System.out.println( this.instance.getName());
}
}
Then use Spring to initialized the bean:
<bean id="consumer" class="com.demo.Consumer" init-method="init">
<property name="instance" ref="userTwo"></property>
</bean>
Please let me know if this the the right approach or if I did something wrong, because I have to use Spring to initialize some beans.
A "right approach" is probably to keep things simple and use Spring's DI if you use Spring Framework, and not try to mix and match with Guice :-)
Having said that there seems no technical reason why they can't be mixed and matched together to some degree.
I think you will have more success with another approach. One that I have used before is to make use of Spring MVC Java-based configuration. Here is the basic approach.
Create a class that extends WebMvcConfigurationSupport:
#Configuration
#Import(BeansConfig.class)
public class Config extends WebMvcConfigurationSupport {
}
Separate out your beans config (probably it can be merged with the above but I guess it's quite dull code and you normally don't want want to see it). And use it to create your beans with your Guice injector before providing them to Spring.
#Configuration
public class BeansConfig {
#Bean
public Consumer getConsumer() {
return SomeGuiceInjectorFactory.newInstance(Consumer.class);
}
}
Include this in your spring.xml (or bootstrap other ways if your servlet container is newer than mine was)
<context:annotation-config/>
<bean id="extendedWebMvcConfig" class="Config"/>
Constructor injection and most/all? other Guice goodness should work also with such scenario.
Also you won't need to configure your beans in xml.