NotSerializableException для Джексона ObjectNode в проблеме закрытия Spark
Скажем, у меня есть следующий Java-объект, который сопоставляется с полной привязкой данных Джексона:
public class Student implements Serializable{
private ObjectNode name; // two keys: "first_name", "last_name"
// getter and setter ...
}
И у меня есть следующий код Spark, который пытается сериализовать переменную замыкания student
типа Student
из-за разных областей применения.
class A(student : Student) extends Serializable {
def process(input: DataFrame): Unit = {
val test = input.map { a =>
print(student)
}
}
}
что дает следующую ошибку: Caused by: java.io.NotSerializableException: com.fasterxml.jackson.databind.node.ObjectNode
Я понимаю, почему я получаю такую ошибку. По сути, Spark попытается сериализовать все переменные, выходящие за пределы области видимости, или замыкания, и передать их каждому исполнителю. Но так как сам ObjectNode не является Serializable
Исполнитель не может получить Student
экземпляров.
У меня вопрос, как я могу решить эту проблему?
Я пытался использовать Map<String, String>
вместо ObjectNode
, но с тех пор ObjectNode
"s put
а также set
может иметь только "примитивы" и JsonNode
как значение, это вызывает ошибку, когда я пытаюсь что-то вроде этого:
ObjectNode meta_info = JsonNodeFactory.instance.objectNode();
meta_info.set("field name", student.getName());
2 ответа
Есть несколько вариантов.
Если вам нужен объектный узел только для целей сериализации json, вы можете переписать Student
класс и полностью удалить ObjectNode
, В вашем примере вы можете заменить его объектом с firstName
а также lastName
поля
class Name implements Serializable {
String firstName;
String lastName;
}
Однако, если это невозможно, вы можете сделать пользовательскую сериализацию для этого
public class Student implements Serializable {
private transient ObjectNode name;
private void writeObject(ObjectOutputStream out) throws IOException {
ObjectMapper mapper = new ObjectMapper();
out.writeUTF(mapper.writeValueAsString(name));
// other fields here
}
private void readObject(ObjectInputStream in) throws IOException,
ClassNotFoundException {
ObjectMapper mapper = new ObjectMapper();
JsonNode node = mapper.readTree(in.readUTF());
if (!node.isObject()) {
throw new IOException("malformed name field detected");
}
name = (ObjectNode) node;
// read other fields
}
}
В моем примере я сериализовал узел объекта в строку json, но вы, конечно, можете перебирать поля узла объекта, сохраняя каждое поле отдельно.
Вы можете прочитать больше о пользовательской сериализации в ObjectOutputStream
Javadoc.
Также вы можете поэкспериментировать с различными сериализаторами данных, такими как Kryo.
Я закончил тем, что сделал student
в Map<String, String>
возражать и делать mapper.convertValue(student, ObjectNode.class
всякий раз, когда мне нужно было быть в ObjectNode
Обновление до jackson-databind до 10.x помогает, обратитесь к этому https://github.com/FasterXML/jackson-databind/issues/18