i'm using the example from this repo https://github.com/kojenov/serial/tree/master/3-4.%20upload, which is presenting a method for specifying a way to protect form unsafe deserialization in Java by defining a custom ObjectInputStream and overriding a protected method resolveClass in which we have to specify which classes are allowed for deserialisation.
My problem is I added a LocalDate field to the Planet class and when I deserialize a serialized object I get this exception:
invalid class except unsupported class; java.time.Ser
I searched online and I could not find any other encounter with that problem, so I'm really confused. I tried with instead of the LocalDate to add a LocalDateTime, the same error happens again. As far as I found that class java.time.Ser is a protected class somewhere in the hierarchy of the classes in that package.
The class LocalDate is serializable, so this should not happen. I know for sure that the problem is in the LocalDate, because if I make that field transient code works as intended. Am I missing something or it's just a bug of Java Object Serialization?
By the way, the examples are originally from a talk given by Alexei Kojenov, his site is kojenov.com, but i couldn't find his email to ask him personally.
Serialization is recursive progress, which means when you're serializing a complex object, firstly you need to serialize all its properties. The same thing happens with deserialization.
Planet object contains fields of type int, double and java.lang.String which are primitives and don't need special (de)serialization. LocalDate or LocalDateTime aren't primitives and they're serialized and then deserialized with SafeObjectInputStream.
Serialization hack
As it said in java.io.Serializable documentation, objects can modify their serialization behaviour and even delegate serialization to another class by defining method writeReplace.
JavaDoc cite:
Serializable classes that need to designate an alternative object to be used when writing an object to the stream should implement this special method with the exact signature:
ANY-ACCESS-MODIFIER Object writeReplace() throws ObjectStreamException;
This writeReplace method is invoked by serialization if the method exists and it would be accessible from a method defined within the class of the object being serialized. Thus, the method can have private, protected and package-private access. Subclass access to this method follows java accessibility rules.
Both LocalDate and LocalDateTime utilizes this possibility and define writeReplace method.
As an example, java.time.LocalDate's implementation:
private Object writeReplace() {
return new Ser(Ser.LOCAL_DATE_TYPE, this);
}
java.time.Ser is a package-private final class that is used as a delegate for java.time.* objects.
Hereby, when you're serializing java.time.LocalDate or java.time.LocalDateTime, actually java.time.Ser being serialized.
Custom deserializer
Previously we found out that java.time.LocalDate was serialized as java.time.Ser. Now, let's try to deserialize it with SafeObjectInputStream.
Before deserialization, resolveClass method is called:
#Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!input.getName().equals(Planet.class.getName())) {
throw new InvalidClassException("Unsupported class", input.getName());
}
return super.resolveClass(input);
}
It checks for class name to be equal to Planet.class.getName(), but java.time.Ser is not, that's why you're getting an exception.
Solution
To resolve this issue, you need to add java.time.Ser to the list of trusted classes. I would suggest modifying your SafeObjectInputStream next way:
public class SafeObjectInputStream extends ObjectInputStream {
private final List<String> supportedClasses = List.of(Planet.class.getName(), "java.time.Ser");
public SafeObjectInputStream(InputStream inputStream) throws IOException {
super(inputStream);
}
#Override
protected Class<?> resolveClass(ObjectStreamClass input)
throws IOException, ClassNotFoundException
{
if (!supportedClasses.contains(input.getName())) {
throw new InvalidClassException("Unsupported class ", input.getName());
}
return super.resolveClass(input);
}
}
NOTE: List.of was introduced in Java 9. If your Java version is less than 9, you can replace it with Arrays.asList.
Related
I am working on building a music.player and have my music-library stored in a HashMap. The User shall be able to add and delete songs. I want to save this HashMap for when the program is beeing restartet.
However did I encounter this warning:
Exception in thread "main" java.io.WriteAbortedException: writing aborted; java.io.NotSerializableException: musicplayer.Song
Research showed I have to implement the Serializable Interface in my Song class. Which I did, but with still this warning.
My Song class:
package musicplayer;
//Song-Klasse, speichert alle Attribute und Methoden eines Songs. Funktioniert soweit
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
public class Song implements Serializable {
private static final long serialVersionUID = 4390482518182625971L;
//Attribute
File file;
Clip clip;
String string;
//...
The MusicDaten - Class
package musicplayer;
public class MusicDaten implements Serializable {
private static Map<String,Song> all; //= new HashMap<String,Song>();
private File file = new File("C://Users//ThinkPad T450s//git//testproject//musicplayer//SongInfo.ser");
// ...
public MusicDaten() throws ClassNotFoundException, IOException {
this.setSavedSongs();
}
public void setSavedSongs() throws IOException, ClassNotFoundException { //initialisziert HashMap mit den gespeicherten Songs
FileInputStream fileIn = new FileInputStream(file);
ObjectInputStream in = new ObjectInputStream(fileIn);
all = (HashMap<String,Song>) in.readObject();
in.close();
fileIn.close();
}
public void save() throws IOException { //Speicher HashMap
FileOutputStream fileOut = new FileOutputStream(file);
ObjectOutputStream out = new ObjectOutputStream(fileOut);
out.writeObject(all);
out.close();
fileOut.close();
System.out.println("Songinfo saved");
}
Thank you for the help.
(I have edited this question since before it wasn't quite clear)
Implementing Serializable is not sufficient.
If you attempt to serialize an object, all its non-transient attributes are serialized, too. If any of those attributes is not Serializable, it will not work.
In your case, Song contains an attribute of type File and File is not serializable. With Clip, you have the same problem.
In order to get around this, you can do custom serialization.
Looking at the docs of Serializable, you can find this:
Classes that require special handling during the serialization and deserialization process must implement special methods with these exact signatures:
private void writeObject(java.io.ObjectOutputStream out)
throws IOException
private void readObject(java.io.ObjectInputStream in)
throws IOException, ClassNotFoundException;
private void readObjectNoData()
throws ObjectStreamException;
The writeObject method is responsible for writing the state of the object for its particular class so that the corresponding readObject method can restore it. The default mechanism for saving the Object's fields can be invoked by calling out.defaultWriteObject. The method does not need to concern itself with the state belonging to its superclasses or subclasses. State is saved by writing the individual fields to the ObjectOutputStream using the writeObject method or by using the methods for primitive data types supported by DataOutput.
The readObject method is responsible for reading from the stream and restoring the classes fields. It may call in.defaultReadObject to invoke the default mechanism for restoring the object's non-static and non-transient fields.
This means that you can create the methods writeObject and readObject where you specify how to (de)serialize the object.
If you want to keep the default (de)serialization of the attributes supporting serialization, you can mark all fields not supporting serialization transient and call out.defaultWriteObject/in.defaultReadObject in the writeObject/readObject methods.
Marking an attribute transient means that serialization ignores it. You can then use your custom logic.
Note that serialization comes with some problems and you might not want to use it.
On one hand, it can lead to serious denial of service and even remote code execution vulnerabilities if you deserialize untrusted data. This is also noted in the docs of Serializable:
Warning: Deserialization of untrusted data is inherently dangerous and should be avoided. Untrusted data should be carefully validated according to the "Serialization and Deserialization" section of the Secure Coding Guidelines for Java SE. Serialization Filtering describes best practices for defensive use of serial filters.
Another problem of serialization is that it binds your application to a fixed format and makes it difficult to be compatible with old serialized data when update your application if you didn't carefully think it through when initially creating it.
For more information about this, you may want to consider reading the book Effective Java.
If you try to serialize an object that has a field of type java.lang.Class, serializing it will lead to java.lang.UnsupportedOperationException: Attempted to serialize java.lang.Class: <some_class> Forgot to register a type adapter
Below is the code snippet from com.google.gson.internal.bind.TypeAdapters.java
public final class TypeAdapters {
.
.
.
public static final TypeAdapter<Class> CLASS = new TypeAdapter<Class>() {
#Override
public void write(JsonWriter out, Class value) throws IOException {
if (value == null) {
out.nullValue();
} else {
throw new UnsupportedOperationException("Attempted to serialize java.lang.Class: "
+ value.getName() + ". Forgot to register a type adapter?");
}
}
.
.
.
}
Was this coded in gson just to remind people if they "Forgot to register a type adapter"?
As I see it, Class type object could have easily been serialized and deserialized using the following statements:
Serialize : clazz.getName()
Deserialize : Class.forName(className)
What could the reason behind the current implementation be? Where am I wrong in this?
as answered by #Programmer Bruce Gson not parsing Class variable -
In a comment in issue 340, a Gson project manager explains:
Serializing types is actually somewhat of a security problem, so we
don't want to support it by default. A malicious .json file could
cause your application to load classes that it wouldn't otherwise;
depending on your class path loading certain classes could DoS your
application.
But it's quite straightforward to write a type adapter to support this
in your own app.
Of course, since serialization is not the same as
deserialization, I don't understand how this is an explanation for the
disabled serialization, unless the unmentioned notion is to in a sense
"balance" the default behaviors of serialization with deserialization.
If you try to marshal a class which references a complex type that does not have a no-arg constructor, such as:
import java.sql.Date;
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
Date d; //java.sql.Date does not have a no-arg constructor
}
with the JAXB implementation that is part of Java, as follows:
Foo foo = new Foo();
JAXBContext jc = JAXBContext.newInstance(Foo.class);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
Marshaller marshaller = jc.createMarshaller();
marshaller.marshal(foo, baos);
JAXB will throw a
com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 1 counts of IllegalAnnotationExceptions java.sql.Date does not have a no-arg default constructor
Now, I understand why JAXB needs a no-arg constructor on unmarshalling - because it needs to instantiate the object. But why does JAXB need a no-arg constructor while marshalling?
Also, another nit, why does Java's JAXB implementation throw an exception if the field is null, and isn't going to be marshalled anyway?
Am I missing something or are these just bad implementation choices in Java's JAXB implementation?
When a JAXB (JSR-222) implementation initializes its metadata it ensures that it can support both marshalling and unmarshalling.
For POJO classes that do not have a no-arg constructor you can use a type level XmlAdapter to handle it:
http://blog.bdoughan.com/2010/12/jaxb-and-immutable-objects.html
java.sql.Date is not supported by default (although in EclipseLink JAXB (MOXy) it is). This can also be handled using an XmlAdapter specified via #XmlJavaTypeAdapter at field, property, or package level:
http://blog.bdoughan.com/2011/05/jaxb-and-joda-time-dates-and-times.html
http://blog.bdoughan.com/2011/01/jaxb-and-datetime-properties.html
Also, another nit, why does Java's JAXB implementation throw an
exception if the field is null, and isn't going to be marshalled
anyway?
What exception are you seeing? Normally when a field is null it is not included in the XML result, unless it is annotated with #XmlElement(nillable=true) in which case the element will include xsi:nil="true".
UPDATE
You could do the following:
SqlDateAdapter
Below is an XmlAdapter that will convert from the java.sql.Date that your JAXB implementation doesn't know how to handle to a java.util.Date which it does:
package forum9268074;
import javax.xml.bind.annotation.adapters.*;
public class SqlDateAdapter extends XmlAdapter<java.util.Date, java.sql.Date> {
#Override
public java.util.Date marshal(java.sql.Date sqlDate) throws Exception {
if(null == sqlDate) {
return null;
}
return new java.util.Date(sqlDate.getTime());
}
#Override
public java.sql.Date unmarshal(java.util.Date utilDate) throws Exception {
if(null == utilDate) {
return null;
}
return new java.sql.Date(utilDate.getTime());
}
}
Foo
The XmlAdapter is registered via the #XmlJavaTypeAdapter annotation:
package forum9268074;
import java.sql.Date;
import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
#XmlRootElement(name = "Foo")
#XmlAccessorType(XmlAccessType.FIELD)
public class Foo {
int i;
#XmlJavaTypeAdapter(SqlDateAdapter.class)
Date d; //java.sql.Date does not have a no-arg constructor
}
To answer your question: I think this is just poor design in JAXB (or perhaps in JAXB implementations). The existence of a no-arg constructor gets validated during creation of the JAXBContext and therefore applies regardless if you want to use JAXB for marshalling or unmarshalling. It would have been great if JAXB would defer this type of check to JAXBContext.createUnmarshaller(). I think it would be interesting to dig into if this design is actually mandated by the spec or if it is an implementation design in JAXB-RI.
But there's indeed a workaround.
JAXB doesn't actually need a no-arg constructor for marshalling. In the following I'll assume you are using JAXB solely for marshalling, not unmarshalling. I also assume that you have control over the immutable object which you want to marshall so that you can change it. If this is not the case then the only way forward is XmlAdapter as described in other answers.
Suppose you have a class, Customer, which is an immutable object. Instantiation is via Builder Pattern or static methods.
public class Customer {
private final String firstName;
private final String lastName;
private Customer(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
// Object created via builder pattern
public static CustomerBuilder createBuilder() {
...
}
// getters here ...
}
True, that by default you cannot get JAXB to unmarshall such an object. You'll get error "....Customer does not have a no-arg default constructor".
There are at least two ways of solving this. They both rely on putting in a method or constructor solely to make JAXB's introspection happy.
Solution 1
In this method we tell JAXB that there's a static factory method it can use to instantiate an instance of the class. We know, but JAXB doesn't, that indeed this will never be used. The trick is the #XmlType annotation with factoryMethod parameter. Here's how:
#XmlType(factoryMethod="createInstanceJAXB")
public class Customer {
...
private static Customer createInstanceJAXB() { // makes JAXB happy, will never be invoked
return null; // ...therefore it doesn't matter what it returns
}
...
}
It doesn't matter if the method is private as in the example. JAXB will still accept it. Your IDE will flag the method as unused if you make it private, but I still prefer private.
Solution 2
In this solution we add a private no-arg constructor which just passes null into the real constructor.
public class Customer {
...
private Customer() { // makes JAXB happy, will never be invoked
this(null, null); // ...therefore it doesn't matter what it creates
}
...
}
It doesn't matter if the constructor is private as in the example. JAXB will still accept it.
Summary
Both solutions satisfies JAXB's desire for no-arg instantiation. It is a shame that we need to do this, when we know by ourselves that we only need to marshal, not unmarshal.
I have to admit that I do not know to what extent this is a hack that will only work with JAXB-RI and not for example with EclipseLink MOXy. It definitely works with JAXB-RI.
You seem to be under the impression that the JAXB introspection code will have action specific paths for initialization. if so, that would result in a lot of duplicate code and would be a poor implementation. i would imagine that the JAXB code has a common routine which examines a model class the first time it is needed and validates that it follows all the necessary conventions. in this situation, it is failing because one of the members does not have the required no-arg constructor. the initialization logic is most likely not marshall/unmarshall specific and also highly unlikely to take the current object instance into account.
Some enterprise and Dependency Injection frameworks use reflection Class.newInstance() to create a new instance of your classes. This method requires a public no-arg constructor to be able to instantiate the object.
I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info
I am currently using Jackson 1.4.2 and attempting deserialization of code values (unique identifiers for type information) that are passed from our UI back to the Java controllers (Servlets).
There are multiple types (e.g. ABCType, XYZType, etc.) that all extend from an AbstractType, but each concrete type has a static factory method that takes as a single parameter, a unique identifier, and returns the type object (name, associated types, description, valid acronyms, etc.) represented by that identifier. The static method within each concrete type (e.g. XYZType) is annotated with #JsonCreator:
#JsonCreator
public static XYZType getInstance(String code) {
.....
}
The problem that I am seeing though is an exception thrown by Jackson's mapper trying to deserialize the json to those types:
Caused by: org.codehaus.jackson.map.JsonMappingException: No default constructor found for type [simple type, class com.company.type.XYZtype]: can not instantiate from Json object.
What am I missing here of the #JsonCreator annotation to static factory methods (or is it to do with Jackson 1.4.2 struggling with the concrete types extending from an AbstractType?)?
The annotation #JsonCreator requires the annotation #JsonProperty. This Jackson wiki page gives little information but does offer sample code:
#JsonCreator
public Name(#JsonProperty("givenName") String g, #JsonProperty("familyName") String f)
{
givenName = g;
familyName = f;
}
You'll find a more detailed explanation at this blog post.
Your sample code should therefore look something like this:
#JsonCreator
public static XYZType getInstance(#JsonProperty("someCode") String code)
{
...
}
Problem is that Jackson only sees the declared base type, and does not know where to look for subtypes.
Since full polymorphic type handling was added in 1.5, what you need to do with 1.4 is to add factory method in the base class and dispatch methods from there.