-
Notifications
You must be signed in to change notification settings - Fork 16
Annotations
When Piriti generates the reader / writer implementation for a type T, all fields of T and its superclasses which satisfy the following requirements are processed:
- The field is accessible. If the reader / writer interface is defined as nested static class or in the same package, the field must have at least default access. If the field isn't accessible, there has to be an appropriate getter / setter.
- The fields type must be supported by Piriti
- The field must not be transient or marked with the @Transient annotation
Suppose you have the following types (all in the same package):
public abstract class Creature
{
String kind;
}
public abstract class Animal extends Creature
{
String name;
int age;
private String state;
}
public abstract class Mammal extends Animal
{
private Gender gender;
public Gender getGender()
{
return gender;
}
}
@MapUpTo(Animal.class)
public class Cat extends Mammal
{
transient int size;
@Transient String race;
String color;
List<Cat> kitten;
Map<String, String> metadata;
}
and this reader definitions
public CatReader extends JsonReader<Cat> {}
Then the following fields are mapped by this reader:
- name
- age
- gender
- color
- kitten
These fields are not processed:
- kind: It's in a class out of reach (excluded by @MapUpTo)
- state: Field is private and no accessible getter was defined
- size: Field is transient
- race: Field was marked with @Transient
- metadata: Type is not supported
By default the fields name is taken as the key / path to the JSON / XML data. That is in the above example there has to be a relevant key / element for the color field:
{
...
"color": "dark grey",
...
}
<cat>
...
<color>dark grey</color>
...
</cat>
The dafault behaviour can be customized using a handful of annotations:
Use the @Path annotation to specify a special JSONPath / XPath. The selected data must match the fields type.
Use this annotation to specify a custom converter. Converters must implement the interface name.pehl.piriti.converter.client.Converter<T
> and are responsible for converting strings to T and vice versa. Piriti already comes with a list of built in converters which handles most of the primitive / wrapper types and dates.
With the help of the @Format annotation you can either specify the format for the built in or custom converter.
Use this annotation to specify how to handle leading and trailing whitespace. You can choose one of the following:
- PRESERVE
- REMOVE_WHITESPACE
- REMOVE_NEWLINE
- REMOVE (default)
Please note that this is only about leading and trailing whitespace. Whitespace within the data is not touched.
Use these annotations to specify a custom Getter and/or Setter implementation. See Getters / Setters for further details.
Using this annotation you can specify an InstanceCreator implementation which is responsible for creating new instances of your POJO. To use an InstanceCreator you have to place a @CreateWith annotation to your POJO class or in case of external mappings to your reader interface. The InstanceCreator is called with a context which is a JSONValue for JSON input and a Node for XML data. InstanceCreators can be useful when your POJO has no default constructor.
Please note that an InstanceCreator is only useful for POJOs with an own reader. To customize the creation of other instances like dates, strings, numbers use a custom converter.
<book>
<isbn>978-0345417954</isbn>
<title>The Hotel New Hampshire</title>
...
</book>
@CreateWith(BookkCreator.class)
public class Book
{
private final String isbn;
private String title;
public Book(String isbn)
{
this.isbn = isbn;
}
// Getters & Setters omitted
}
public class BookkCreator extends XmlInstanceCreator<Book>
{
public Book newInstance(Node context)
{
String isbn = context.selectValue("isbn");
return new Book(isbn);
}
}
The only exception to this is the mapping of polymorphic references. In this case you can use an InstanceCreator and the @CreateWith annotation for arrays and collections. The element type of the array / collection must be in turn a POJO with an own reader.
When (de)serializing a POJO all of it fields and the fields in its superclasses are processed. Sometimes this is not what you want. Using the @!MapUpTo annotation you can control up to which class the fields should be processed. See Inheritance for further details.
Piriti processes all fields in random order. If you depend on a specific order, you can use the @Order annotation. Suppose you have the following POJO:
public class User
{
@Order(0) String username;
@Order(2) String firstname;
@Order(1) String surname;
int age;
boolean valid;
}
When reading / writing the fields are proceesed in this order
- username
- surname
- firstname
- age or valid
- age or valid (order is not specified)
When (de)serializing a POJO all non-transient (the keyword transient) fields are processed. If you don't want to declare a field as transient you can still annotate the field with @Transient to exclude it during (de)serialization.
Fields annotated with @Native are processed during (de)serialization, but not mapped. This is kind of last resort when Piriti is not able to map the data. Fields annotated with @Native must have one of the follwoing types:
- String (for both XML and JSON mappings)
- JSONValue or subclass of JSONValue
- name.pehl.totoe.xml.client.Node or sublcass of Node
Given the XML input
<book>
<isbn>0815</isbn>
<metadata>
<key>1</key>
<value>foo</value>
<key>flag</key>
<value>true</value>
...
</metadata>
</book>
and the POJO
public class Book
{
String isbn;
@Native Element metadata;
}
you can access the native XML element using Book.metadata