@Override
    public JsonElement serialize(
        OutputDataRaw cellRaw, Type typeOfSrc, JsonSerializationContext context) {
      final JsonObject jsonObject = new JsonObject();

      if (cellRaw.png != null) {
        jsonObject.addProperty("image/png", cellRaw.png);
      }
      if (cellRaw.html != null) {
        final JsonElement html = gson.toJsonTree(cellRaw.html);
        jsonObject.add("text/html", html);
      }
      if (cellRaw.svg != null) {
        final JsonElement svg = gson.toJsonTree(cellRaw.svg);
        jsonObject.add("image/svg+xml", svg);
      }
      if (cellRaw.jpeg != null) {
        final JsonElement jpeg = gson.toJsonTree(cellRaw.jpeg);
        jsonObject.add("image/jpeg", jpeg);
      }
      if (cellRaw.latex != null) {
        final JsonElement latex = gson.toJsonTree(cellRaw.latex);
        jsonObject.add("text/latex", latex);
      }
      if (cellRaw.text != null) {
        final JsonElement text = gson.toJsonTree(cellRaw.text);
        jsonObject.add("text/plain", text);
      }

      return jsonObject;
    }
    @Override
    public CellOutputRaw deserialize(
        JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
      final JsonObject object = json.getAsJsonObject();
      final CellOutputRaw cellOutputRaw = new CellOutputRaw();
      final JsonElement ename = object.get("ename");
      if (ename != null) {
        cellOutputRaw.ename = ename.getAsString();
      }
      final JsonElement name = object.get("name");
      if (name != null) {
        cellOutputRaw.name = name.getAsString();
      }
      final JsonElement evalue = object.get("evalue");
      if (evalue != null) {
        cellOutputRaw.evalue = evalue.getAsString();
      }
      final JsonElement data = object.get("data");
      if (data != null) {
        cellOutputRaw.data = gson.fromJson(data, OutputDataRaw.class);
      }

      final JsonElement count = object.get("execution_count");
      if (count != null) {
        cellOutputRaw.execution_count = count.getAsInt();
      }
      final JsonElement outputType = object.get("output_type");
      if (outputType != null) {
        cellOutputRaw.output_type = outputType.getAsString();
      }
      final JsonElement png = object.get("png");
      if (png != null) {
        cellOutputRaw.png = png.getAsString();
      }
      final JsonElement stream = object.get("stream");
      if (stream != null) {
        cellOutputRaw.stream = stream.getAsString();
      }
      final JsonElement jpeg = object.get("jpeg");
      if (jpeg != null) {
        cellOutputRaw.jpeg = jpeg.getAsString();
      }

      cellOutputRaw.html = getStringOrArray("html", object);
      cellOutputRaw.latex = getStringOrArray("latex", object);
      cellOutputRaw.svg = getStringOrArray("svg", object);
      final JsonElement promptNumber = object.get("prompt_number");
      if (promptNumber != null) {
        cellOutputRaw.prompt_number = promptNumber.getAsInt();
      }
      cellOutputRaw.text = getStringOrArray("text", object);
      cellOutputRaw.traceback = getStringOrArray("traceback", object);
      final JsonElement metadata = object.get("metadata");
      if (metadata != null) {
        cellOutputRaw.metadata = gson.fromJson(metadata, Map.class);
      }

      return cellOutputRaw;
    }
 @NotNull
 public static IpnbFile parseIpnbFile(
     @NotNull final CharSequence fileText, @NotNull final VirtualFile virtualFile)
     throws IOException {
   final String path = virtualFile.getPath();
   IpnbFileRaw rawFile = gson.fromJson(fileText.toString(), IpnbFileRaw.class);
   if (rawFile == null) {
     int nbformat = isIpythonNewFormat(virtualFile) ? 4 : 3;
     return new IpnbFile(Collections.emptyMap(), nbformat, Lists.newArrayList(), path);
   }
   List<IpnbCell> cells = new ArrayList<IpnbCell>();
   final IpnbWorksheet[] worksheets = rawFile.worksheets;
   if (worksheets == null) {
     for (IpnbCellRaw rawCell : rawFile.cells) {
       cells.add(rawCell.createCell());
     }
   } else {
     for (IpnbWorksheet worksheet : worksheets) {
       final List<IpnbCellRaw> rawCells = worksheet.cells;
       for (IpnbCellRaw rawCell : rawCells) {
         cells.add(rawCell.createCell());
       }
     }
   }
   return new IpnbFile(rawFile.metadata, rawFile.nbformat, cells, path);
 }
  @Nullable
  public static String newDocumentText(@NotNull final IpnbFilePanel ipnbPanel) {
    final IpnbFile ipnbFile = ipnbPanel.getIpnbFile();
    if (ipnbFile == null) return null;
    for (IpnbEditablePanel panel : ipnbPanel.getIpnbPanels()) {
      if (panel.isModified()) {
        panel.updateCellSource();
      }
    }

    final IpnbFileRaw fileRaw = new IpnbFileRaw();
    fileRaw.metadata = ipnbFile.getMetadata();
    if (ipnbFile.getNbformat() == 4) {
      for (IpnbCell cell : ipnbFile.getCells()) {
        fileRaw.cells.add(IpnbCellRaw.fromCell(cell, ipnbFile.getNbformat()));
      }
    } else {
      final IpnbWorksheet worksheet = new IpnbWorksheet();
      worksheet.cells.clear();
      for (IpnbCell cell : ipnbFile.getCells()) {
        worksheet.cells.add(IpnbCellRaw.fromCell(cell, ipnbFile.getNbformat()));
      }
      fileRaw.worksheets = new IpnbWorksheet[] {worksheet};
    }
    final StringWriter stringWriter = new StringWriter();
    final JsonWriter writer = new JsonWriter(stringWriter);
    writer.setIndent(" ");
    gson.toJson(fileRaw, fileRaw.getClass(), writer);
    return stringWriter.toString();
  }
    @Override
    public IpnbCellRaw deserialize(
        JsonElement json, Type typeOfT, JsonDeserializationContext context)
        throws JsonParseException {
      final JsonObject object = json.getAsJsonObject();
      final IpnbCellRaw cellRaw = new IpnbCellRaw();
      final JsonElement cell_type = object.get("cell_type");
      if (cell_type != null) {
        cellRaw.cell_type = cell_type.getAsString();
      }
      final JsonElement count = object.get("execution_count");
      if (count != null) {
        cellRaw.execution_count = count.isJsonNull() ? null : count.getAsInt();
      }
      final JsonElement metadata = object.get("metadata");
      if (metadata != null) {
        cellRaw.metadata = gson.fromJson(metadata, Map.class);
      }
      final JsonElement level = object.get("level");
      if (level != null) {
        cellRaw.level = level.getAsInt();
      }

      final JsonElement outputsElement = object.get("outputs");
      if (outputsElement != null) {
        final JsonArray outputs = outputsElement.getAsJsonArray();
        cellRaw.outputs = Lists.newArrayList();
        for (JsonElement output : outputs) {
          cellRaw.outputs.add(gson.fromJson(output, CellOutputRaw.class));
        }
      }
      cellRaw.source = getStringOrArray("source", object);
      cellRaw.input = getStringOrArray("input", object);
      final JsonElement language = object.get("language");
      if (language != null) {
        cellRaw.language = language.getAsString();
      }
      final JsonElement number = object.get("prompt_number");
      if (number != null) {
        cellRaw.prompt_number = number.getAsInt();
      }
      return cellRaw;
    }
    @Override
    public JsonElement serialize(
        IpnbCellRaw cellRaw, Type typeOfSrc, JsonSerializationContext context) {
      final JsonObject jsonObject = new JsonObject();
      jsonObject.addProperty("cell_type", cellRaw.cell_type);
      if ("code".equals(cellRaw.cell_type)) {
        final Integer count = cellRaw.execution_count;
        if (count == null) {
          jsonObject.add("execution_count", JsonNull.INSTANCE);
        } else {
          jsonObject.addProperty("execution_count", count);
        }
      }
      if (cellRaw.metadata != null) {
        final JsonElement metadata = gson.toJsonTree(cellRaw.metadata);
        jsonObject.add("metadata", metadata);
      }
      if (cellRaw.level != null) {
        jsonObject.addProperty("level", cellRaw.level);
      }

      if (cellRaw.outputs != null) {
        final JsonElement outputs = gson.toJsonTree(cellRaw.outputs);
        jsonObject.add("outputs", outputs);
      }
      if (cellRaw.source != null) {
        final JsonElement source = gson.toJsonTree(cellRaw.source);
        jsonObject.add("source", source);
      }
      if (cellRaw.input != null) {
        final JsonElement input = gson.toJsonTree(cellRaw.input);
        jsonObject.add("input", input);
      }
      if (cellRaw.language != null) {
        jsonObject.addProperty("language", cellRaw.language);
      }
      if (cellRaw.prompt_number != null) {
        jsonObject.addProperty("prompt_number", cellRaw.prompt_number);
      }

      return jsonObject;
    }
    @Override
    public JsonElement serialize(
        IpnbFileRaw fileRaw, Type typeOfSrc, JsonSerializationContext context) {
      final JsonObject jsonObject = new JsonObject();
      if (fileRaw.worksheets != null) {
        final JsonElement worksheets = gson.toJsonTree(fileRaw.worksheets);
        jsonObject.add("worksheets", worksheets);
      }
      if (fileRaw.cells != null) {
        final JsonElement cells = gson.toJsonTree(fileRaw.cells);
        jsonObject.add("cells", cells);
      }
      final JsonElement metadata = gson.toJsonTree(fileRaw.metadata);
      jsonObject.add("metadata", metadata);

      jsonObject.addProperty("nbformat", fileRaw.nbformat);
      jsonObject.addProperty("nbformat_minor", fileRaw.nbformat_minor);

      return jsonObject;
    }
    @Override
    public JsonElement serialize(
        CellOutputRaw cellRaw, Type typeOfSrc, JsonSerializationContext context) {
      final JsonObject jsonObject = new JsonObject();
      if (cellRaw.ename != null) {
        jsonObject.addProperty("ename", cellRaw.ename);
      }
      if (cellRaw.name != null) {
        jsonObject.addProperty("name", cellRaw.name);
      }
      if (cellRaw.evalue != null) {
        jsonObject.addProperty("evalue", cellRaw.evalue);
      }

      if (cellRaw.data != null) {
        final JsonElement data = gson.toJsonTree(cellRaw.data);
        jsonObject.add("data", data);
      }
      if (cellRaw.execution_count != null) {
        jsonObject.addProperty("execution_count", cellRaw.execution_count);
      }
      if (cellRaw.png != null) {
        jsonObject.addProperty("png", cellRaw.png);
      }

      if (cellRaw.stream != null) {
        jsonObject.addProperty("stream", cellRaw.stream);
      }

      if (cellRaw.jpeg != null) {
        jsonObject.addProperty("jpeg", cellRaw.jpeg);
      }

      if (cellRaw.html != null) {
        final JsonElement html = gson.toJsonTree(cellRaw.html);
        jsonObject.add("html", html);
      }
      if (cellRaw.latex != null) {
        final JsonElement latex = gson.toJsonTree(cellRaw.latex);
        jsonObject.add("latex", latex);
      }

      if (cellRaw.svg != null) {
        final JsonElement svg = gson.toJsonTree(cellRaw.svg);
        jsonObject.add("svg", svg);
      }
      if (cellRaw.prompt_number != null) {
        jsonObject.addProperty("prompt_number", cellRaw.prompt_number);
      }
      if (cellRaw.traceback != null) {
        final JsonElement traceback = gson.toJsonTree(cellRaw.traceback);
        jsonObject.add("traceback", traceback);
      }

      if (cellRaw.metadata != null) {
        final JsonElement metadata = gson.toJsonTree(cellRaw.metadata);
        jsonObject.add("metadata", metadata);
      }
      if (cellRaw.output_type != null) {
        jsonObject.addProperty("output_type", cellRaw.output_type);
      }
      if (cellRaw.text != null) {
        final JsonElement text = gson.toJsonTree(cellRaw.text);
        jsonObject.add("text", text);
      }

      return jsonObject;
    }