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

Другие вопросы по тегам