docs: public static MultiLayerConfiguration fromJson(String json) {
          MultiLayerConfiguration conf;
          ObjectMapper mapper = NeuralNetConfiguration.mapper();
          try {
              conf = mapper.readValue(json, MultiLayerConfiguration.class);
          } catch (IOException e) {
              //Check if this exception came from legacy deserializer...
              String msg = e.getMessage();
              if (msg != null && msg.contains("legacy")) {
                  throw new RuntimeException("Error deserializing MultiLayerConfiguration - configuration may have a custom " +
                          "layer, vertex or preprocessor, in pre version 1.0.0-alpha JSON format. These layers can be " +
                          "deserialized by first registering them with NeuralNetConfiguration.registerLegacyCustomClassesForJSON(Class...)", e);
              }
              throw new RuntimeException(e);
          }
  
  
          //To maintain backward compatibility after loss function refactoring (configs generated with v0.5.0 or earlier)
          // Previously: enumeration used for loss functions. Now: use classes
          // IN the past, could have only been an OutputLayer or RnnOutputLayer using these enums
          int layerCount = 0;
          JsonNode confs = null;
          for (NeuralNetConfiguration nnc : conf.getConfs()) {
              Layer l = nnc.getLayer();
              if (l instanceof BaseOutputLayer && ((BaseOutputLayer) l).getLossFn() == null) {
                  //lossFn field null -> may be an old config format, with lossFunction field being for the enum
                  //if so, try walking the JSON graph to extract out the appropriate enum value
  
                  BaseOutputLayer ol = (BaseOutputLayer) l;
                  try {
                      JsonNode jsonNode = mapper.readTree(json);
                      if (confs == null) {
                          confs = jsonNode.get("confs");
                      }
                      if (confs instanceof ArrayNode) {
                          ArrayNode layerConfs = (ArrayNode) confs;
                          JsonNode outputLayerNNCNode = layerConfs.get(layerCount);
                          if (outputLayerNNCNode == null)
                              return conf; //Should never happen...
                          JsonNode outputLayerNode = outputLayerNNCNode.get("layer");
  
                          JsonNode lossFunctionNode = null;
                          if (outputLayerNode.has("output")) {
                              lossFunctionNode = outputLayerNode.get("output").get("lossFunction");
                          } else if (outputLayerNode.has("rnnoutput")) {
                              lossFunctionNode = outputLayerNode.get("rnnoutput").get("lossFunction");
                          }
  
                          if (lossFunctionNode != null) {
                              String lossFunctionEnumStr = lossFunctionNode.asText();
                              LossFunctions.LossFunction lossFunction = null;
                              try {
                                  lossFunction = LossFunctions.LossFunction.valueOf(lossFunctionEnumStr);
                              } catch (Exception e) {
                                  log.warn("OutputLayer with null LossFunction or pre-0.6.0 loss function configuration detected: could not parse JSON",
                                          e);
                              }
  
                              if (lossFunction != null) {
                                  switch (lossFunction) {
                                      case MSE:
                                          ol.setLossFn(new LossMSE());
                                          break;
                                      case XENT:
                                          ol.setLossFn(new LossBinaryXENT());
                                          break;
                                      case NEGATIVELOGLIKELIHOOD:
                                          ol.setLossFn(new LossNegativeLogLikelihood());
                                          break;
                                      case MCXENT:
                                          ol.setLossFn(new LossMCXENT());
                                          break;
  
                                      //Remaining: TODO
                                      case EXPLL:
                                      case RMSE_XENT:
                                      case SQUARED_LOSS:
                                      case RECONSTRUCTION_CROSSENTROPY:
                                      case CUSTOM:
                                      default:
                                          log.warn("OutputLayer with null LossFunction or pre-0.6.0 loss function configuration detected: could not set loss function for {}",
                                                  lossFunction);
                                          break;
                                  }
                              }
                          }
  
                      } else {
                          log.warn("OutputLayer with null LossFunction or pre-0.6.0 loss function configuration detected: could not parse JSON: layer 'confs' field is not an ArrayNode (is: {})",
                                  (confs != null ? confs.getClass() : null));
                      }
                  } catch (IOException e) {
                      log.warn("OutputLayer with null LossFunction or pre-0.6.0 loss function configuration detected: could not parse JSON",
                              e);
                      break;
                  }
              }
  
              //Also, pre 0.7.2: activation functions were Strings ("activationFunction" field), not classes ("activationFn")
              //Try to load the old format if necessary, and create the appropriate IActivation instance
              if ((l instanceof BaseLayer) && ((BaseLayer) l).getActivationFn() == null) {
                  try {
                      JsonNode jsonNode = mapper.readTree(json);
                      if (confs == null) {
                          confs = jsonNode.get("confs");
                      }
                      if (confs instanceof ArrayNode) {
                          ArrayNode layerConfs = (ArrayNode) confs;
                          JsonNode outputLayerNNCNode = layerConfs.get(layerCount);
                          if (outputLayerNNCNode == null)
                              return conf; //Should never happen...
                          JsonNode layerWrapperNode = outputLayerNNCNode.get("layer");
  
                          if (layerWrapperNode == null || layerWrapperNode.size() != 1) {
                              continue;
                          }
  
                          JsonNode layerNode = layerWrapperNode.elements().next();
                          JsonNode activationFunction = layerNode.get("activationFunction"); //Should only have 1 element: "dense", "output", etc
  
                          if (activationFunction != null) {
                              IActivation ia = Activation.fromString(activationFunction.asText()).getActivationFunction();
                              ((BaseLayer) l).setActivationFn(ia);
                          }
                      }
  
                  } catch (IOException e) {
                      log.warn("Layer with null ActivationFn field or pre-0.7.2 activation function detected: could not parse JSON",
                              e);
                  }
              }
  
              if(!handleLegacyWeightInitFromJson(json, l, mapper, confs, layerCount)) {
                  return conf;
              }
  
              layerCount++;
          }
          return conf;
      }