Implement fromJson() constructors for LSP types

Also add a canParse() method that can check whether some provided JSON can be decoded into that type (used for detecting with type from a union to decode as).
Support equality checks on unions and a valueEquals() helper
Remove brittle tests that do exact comparisons on generated code
Fix deserialisation of lists to be cast and toList()'d
Add a more complete JSON test that includes lists, enums

Change-Id: Id56fdad9b1454e540e55907e0ff2608263a87c40
Reviewed-on: https://dart-review.googlesource.com/c/80580
Reviewed-by: Brian Wilkerson <brianwilkerson@google.com>
Commit-Queue: Danny Tuppeny <dantup@google.com>
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
index 7f93e74..97a2721 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_generated.dart
@@ -11,7 +11,16 @@
 import 'package:analysis_server/lsp_protocol/protocol_special.dart';
 
 class ApplyWorkspaceEditParams {
-  ApplyWorkspaceEditParams(this.label, this.edit);
+  ApplyWorkspaceEditParams(this.label, this.edit) {
+    if (edit == null) {
+      throw 'edit is required but was not provided';
+    }
+  }
+  factory ApplyWorkspaceEditParams.fromJson(Map<String, dynamic> json) {
+    final label = json['label'];
+    final edit = new WorkspaceEdit.fromJson(json['edit']);
+    return new ApplyWorkspaceEditParams(label, edit);
+  }
 
   /// The edits to apply.
   final WorkspaceEdit edit;
@@ -28,10 +37,30 @@
     __result['edit'] = edit ?? (throw 'edit is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('edit') || !WorkspaceEdit.canParse(map['edit'])) {
+      return false;
+    }
+    const validFieldNames = ['label', 'edit'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ApplyWorkspaceEditResponse {
-  ApplyWorkspaceEditResponse(this.applied);
+  ApplyWorkspaceEditResponse(this.applied) {
+    if (applied == null) {
+      throw 'applied is required but was not provided';
+    }
+  }
+  factory ApplyWorkspaceEditResponse.fromJson(Map<String, dynamic> json) {
+    final applied = json['applied'];
+    return new ApplyWorkspaceEditResponse(applied);
+  }
 
   /// Indicates whether the edit was applied or not.
   final bool applied;
@@ -42,10 +71,34 @@
         applied ?? (throw 'applied is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('applied') || !map['applied'] is bool) {
+      return false;
+    }
+    const validFieldNames = ['applied'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class CancelParams {
-  CancelParams(this.id);
+  CancelParams(this.id) {
+    if (id == null) {
+      throw 'id is required but was not provided';
+    }
+  }
+  factory CancelParams.fromJson(Map<String, dynamic> json) {
+    final id = json['id'] is num
+        ? new Either2<num, String>.t1(json['id'])
+        : (json['id'] is String
+            ? new Either2<num, String>.t2(json['id'])
+            : (throw '''${json['id']} was not one of (number, string)'''));
+    return new CancelParams(id);
+  }
 
   /// The request id to cancel.
   final Either2<num, String> id;
@@ -55,10 +108,30 @@
     __result['id'] = id ?? (throw 'id is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('id') || !(map['id'] is num || map['id'] is String)) {
+      return false;
+    }
+    const validFieldNames = ['id'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ClientCapabilities {
   ClientCapabilities(this.workspace, this.textDocument, this.experimental);
+  factory ClientCapabilities.fromJson(Map<String, dynamic> json) {
+    final workspace =
+        new WorkspaceClientCapabilities.fromJson(json['workspace']);
+    final textDocument =
+        new TextDocumentClientCapabilities.fromJson(json['textDocument']);
+    final experimental = json['experimental'];
+    return new ClientCapabilities(workspace, textDocument, experimental);
+  }
 
   /// Experimental client capabilities.
   final dynamic experimental;
@@ -82,6 +155,15 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['workspace', 'textDocument', 'experimental'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// A code action represents a change that can be performed in code, e.g. to fix
@@ -90,7 +172,22 @@
 /// A CodeAction must set either `edit` and/or a `command`. If both are
 /// supplied, the `edit` is applied first, then the `command` is executed.
 class CodeAction {
-  CodeAction(this.title, this.kind, this.diagnostics, this.edit, this.command);
+  CodeAction(this.title, this.kind, this.diagnostics, this.edit, this.command) {
+    if (title == null) {
+      throw 'title is required but was not provided';
+    }
+  }
+  factory CodeAction.fromJson(Map<String, dynamic> json) {
+    final title = json['title'];
+    final kind = json['kind'];
+    final diagnostics = json['diagnostics']
+        ?.map((item) => new Diagnostic.fromJson(item))
+        ?.cast<Diagnostic>()
+        ?.toList();
+    final edit = new WorkspaceEdit.fromJson(json['edit']);
+    final command = new Command.fromJson(json['command']);
+    return new CodeAction(title, kind, diagnostics, edit, command);
+  }
 
   /// A command this code action executes. If a code action provides an edit and
   /// a command, first the edit is executed and then the command.
@@ -127,12 +224,36 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('title') || !map['title'] is String) {
+      return false;
+    }
+    const validFieldNames = ['title', 'kind', 'diagnostics', 'edit', 'command'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Contains additional diagnostic information about the context in which a code
 /// action is run.
 class CodeActionContext {
-  CodeActionContext(this.diagnostics, this.only);
+  CodeActionContext(this.diagnostics, this.only) {
+    if (diagnostics == null) {
+      throw 'diagnostics is required but was not provided';
+    }
+  }
+  factory CodeActionContext.fromJson(Map<String, dynamic> json) {
+    final diagnostics = json['diagnostics']
+        ?.map((item) => new Diagnostic.fromJson(item))
+        ?.cast<Diagnostic>()
+        ?.toList();
+    final only = json['only']?.map((item) => item)?.cast<String>()?.toList();
+    return new CodeActionContext(diagnostics, only);
+  }
 
   /// An array of diagnostics.
   final List<Diagnostic> diagnostics;
@@ -152,6 +273,22 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('diagnostics') ||
+        !(map['diagnostics'] is List &&
+            (map['diagnostics'].length == 0 ||
+                map['diagnostics']
+                    .every((item) => Diagnostic.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['diagnostics', 'only'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// A set of predefined code action kinds
@@ -207,6 +344,11 @@
 /// Code Action options.
 class CodeActionOptions {
   CodeActionOptions(this.codeActionKinds);
+  factory CodeActionOptions.fromJson(Map<String, dynamic> json) {
+    final codeActionKinds =
+        json['codeActionKinds']?.map((item) => item)?.cast<String>()?.toList();
+    return new CodeActionOptions(codeActionKinds);
+  }
 
   /// CodeActionKinds that this server may return.
   ///
@@ -221,11 +363,37 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['codeActionKinds'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Params for the CodeActionRequest
 class CodeActionParams {
-  CodeActionParams(this.textDocument, this.range, this.context);
+  CodeActionParams(this.textDocument, this.range, this.context) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+    if (context == null) {
+      throw 'context is required but was not provided';
+    }
+  }
+  factory CodeActionParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final range = new Range.fromJson(json['range']);
+    final context = new CodeActionContext.fromJson(json['context']);
+    return new CodeActionParams(textDocument, range, context);
+  }
 
   /// Context carrying additional information.
   final CodeActionContext context;
@@ -245,11 +413,40 @@
         context ?? (throw 'context is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    if (!map.containsKey('context') ||
+        !CodeActionContext.canParse(map['context'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'range', 'context'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class CodeActionRegistrationOptions
     implements TextDocumentRegistrationOptions, CodeActionOptions {
   CodeActionRegistrationOptions(this.documentSelector, this.codeActionKinds);
+  factory CodeActionRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    final codeActionKinds =
+        json['codeActionKinds']?.map((item) => item)?.cast<String>()?.toList();
+    return new CodeActionRegistrationOptions(documentSelector, codeActionKinds);
+  }
 
   /// CodeActionKinds that this server may return.
   ///
@@ -269,6 +466,22 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['documentSelector', 'codeActionKinds'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// A code lens represents a command that should be shown along with source
@@ -278,7 +491,17 @@
 /// performance reasons the creation of a code lens and resolving should be done
 /// in two stages.
 class CodeLens {
-  CodeLens(this.range, this.command, this.data);
+  CodeLens(this.range, this.command, this.data) {
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+  }
+  factory CodeLens.fromJson(Map<String, dynamic> json) {
+    final range = new Range.fromJson(json['range']);
+    final command = new Command.fromJson(json['command']);
+    final data = json['data'];
+    return new CodeLens(range, command, data);
+  }
 
   /// The command this code lens represents.
   final Command command;
@@ -302,11 +525,27 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    const validFieldNames = ['range', 'command', 'data'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Code Lens options.
 class CodeLensOptions {
   CodeLensOptions(this.resolveProvider);
+  factory CodeLensOptions.fromJson(Map<String, dynamic> json) {
+    final resolveProvider = json['resolveProvider'];
+    return new CodeLensOptions(resolveProvider);
+  }
 
   /// Code lens has a resolve provider as well.
   final bool resolveProvider;
@@ -318,10 +557,28 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['resolveProvider'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class CodeLensParams {
-  CodeLensParams(this.textDocument);
+  CodeLensParams(this.textDocument) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+  }
+  factory CodeLensParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    return new CodeLensParams(textDocument);
+  }
 
   /// The document to request code lens for.
   final TextDocumentIdentifier textDocument;
@@ -332,10 +589,31 @@
         textDocument ?? (throw 'textDocument is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class CodeLensRegistrationOptions implements TextDocumentRegistrationOptions {
   CodeLensRegistrationOptions(this.resolveProvider, this.documentSelector);
+  factory CodeLensRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final resolveProvider = json['resolveProvider'];
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new CodeLensRegistrationOptions(resolveProvider, documentSelector);
+  }
 
   /// A document selector to identify the scope of the registration. If set to
   /// null the document selector provided on the client side will be used.
@@ -352,11 +630,47 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['resolveProvider', 'documentSelector'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Represents a color in RGBA space.
 class Color {
-  Color(this.red, this.green, this.blue, this.alpha);
+  Color(this.red, this.green, this.blue, this.alpha) {
+    if (red == null) {
+      throw 'red is required but was not provided';
+    }
+    if (green == null) {
+      throw 'green is required but was not provided';
+    }
+    if (blue == null) {
+      throw 'blue is required but was not provided';
+    }
+    if (alpha == null) {
+      throw 'alpha is required but was not provided';
+    }
+  }
+  factory Color.fromJson(Map<String, dynamic> json) {
+    final red = json['red'];
+    final green = json['green'];
+    final blue = json['blue'];
+    final alpha = json['alpha'];
+    return new Color(red, green, blue, alpha);
+  }
 
   final num alpha;
   final num blue;
@@ -371,10 +685,43 @@
     __result['alpha'] = alpha ?? (throw 'alpha is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('red') || !map['red'] is num) {
+      return false;
+    }
+    if (!map.containsKey('green') || !map['green'] is num) {
+      return false;
+    }
+    if (!map.containsKey('blue') || !map['blue'] is num) {
+      return false;
+    }
+    if (!map.containsKey('alpha') || !map['alpha'] is num) {
+      return false;
+    }
+    const validFieldNames = ['red', 'green', 'blue', 'alpha'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ColorInformation {
-  ColorInformation(this.range, this.color);
+  ColorInformation(this.range, this.color) {
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+    if (color == null) {
+      throw 'color is required but was not provided';
+    }
+  }
+  factory ColorInformation.fromJson(Map<String, dynamic> json) {
+    final range = new Range.fromJson(json['range']);
+    final color = new Color.fromJson(json['color']);
+    return new ColorInformation(range, color);
+  }
 
   /// The actual color value for this color range.
   final Color color;
@@ -388,10 +735,38 @@
     __result['color'] = color ?? (throw 'color is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    if (!map.containsKey('color') || !Color.canParse(map['color'])) {
+      return false;
+    }
+    const validFieldNames = ['range', 'color'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ColorPresentation {
-  ColorPresentation(this.label, this.textEdit, this.additionalTextEdits);
+  ColorPresentation(this.label, this.textEdit, this.additionalTextEdits) {
+    if (label == null) {
+      throw 'label is required but was not provided';
+    }
+  }
+  factory ColorPresentation.fromJson(Map<String, dynamic> json) {
+    final label = json['label'];
+    final textEdit = new TextEdit.fromJson(json['textEdit']);
+    final additionalTextEdits = json['additionalTextEdits']
+        ?.map((item) => new TextEdit.fromJson(item))
+        ?.cast<TextEdit>()
+        ?.toList();
+    return new ColorPresentation(label, textEdit, additionalTextEdits);
+  }
 
   /// An optional array of additional text edits ([TextEdit]) that are applied
   /// when selecting this color presentation. Edits must not overlap with the
@@ -419,10 +794,39 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('label') || !map['label'] is String) {
+      return false;
+    }
+    const validFieldNames = ['label', 'textEdit', 'additionalTextEdits'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ColorPresentationParams {
-  ColorPresentationParams(this.textDocument, this.color, this.range);
+  ColorPresentationParams(this.textDocument, this.color, this.range) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (color == null) {
+      throw 'color is required but was not provided';
+    }
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+  }
+  factory ColorPresentationParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final color = new Color.fromJson(json['color']);
+    final range = new Range.fromJson(json['range']);
+    return new ColorPresentationParams(textDocument, color, range);
+  }
 
   /// The color information to request presentations for.
   final Color color;
@@ -441,6 +845,25 @@
     __result['range'] = range ?? (throw 'range is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('color') || !Color.canParse(map['color'])) {
+      return false;
+    }
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'color', 'range'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Color provider options.
@@ -449,10 +872,33 @@
     Map<String, dynamic> __result = {};
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = [''];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class Command {
-  Command(this.title, this.command, this.arguments);
+  Command(this.title, this.command, this.arguments) {
+    if (title == null) {
+      throw 'title is required but was not provided';
+    }
+    if (command == null) {
+      throw 'command is required but was not provided';
+    }
+  }
+  factory Command.fromJson(Map<String, dynamic> json) {
+    final title = json['title'];
+    final command = json['command'];
+    final arguments =
+        json['arguments']?.map((item) => item)?.cast<dynamic>()?.toList();
+    return new Command(title, command, arguments);
+  }
 
   /// Arguments that the command handler should be invoked with.
   final List<dynamic> arguments;
@@ -473,12 +919,36 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('title') || !map['title'] is String) {
+      return false;
+    }
+    if (!map.containsKey('command') || !map['command'] is String) {
+      return false;
+    }
+    const validFieldNames = ['title', 'command', 'arguments'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Contains additional information about the context in which a completion
 /// request is triggered.
 class CompletionContext {
-  CompletionContext(this.triggerKind, this.triggerCharacter);
+  CompletionContext(this.triggerKind, this.triggerCharacter) {
+    if (triggerKind == null) {
+      throw 'triggerKind is required but was not provided';
+    }
+  }
+  factory CompletionContext.fromJson(Map<String, dynamic> json) {
+    final triggerKind = new CompletionTriggerKind.fromJson(json['triggerKind']);
+    final triggerCharacter = json['triggerCharacter'];
+    return new CompletionContext(triggerKind, triggerCharacter);
+  }
 
   /// The trigger character (a single character) that has trigger code complete.
   /// Is undefined if `triggerKind !== CompletionTriggerKind.TriggerCharacter`
@@ -496,6 +966,19 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('triggerKind') ||
+        !CompletionTriggerKind.canParse(map['triggerKind'])) {
+      return false;
+    }
+    const validFieldNames = ['triggerKind', 'triggerCharacter'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class CompletionItem {
@@ -514,7 +997,54 @@
       this.additionalTextEdits,
       this.commitCharacters,
       this.command,
-      this.data);
+      this.data) {
+    if (label == null) {
+      throw 'label is required but was not provided';
+    }
+  }
+  factory CompletionItem.fromJson(Map<String, dynamic> json) {
+    final label = json['label'];
+    final kind = new CompletionItemKind.fromJson(json['kind']);
+    final detail = json['detail'];
+    final documentation = json['documentation'] is String
+        ? new Either2<String, MarkupContent>.t1(json['documentation'])
+        : (MarkupContent.canParse(json['documentation'])
+            ? new Either2<String, MarkupContent>.t2(
+                new MarkupContent.fromJson(json['documentation']))
+            : (throw '''${json['documentation']} was not one of (string, MarkupContent)'''));
+    final deprecated = json['deprecated'];
+    final preselect = json['preselect'];
+    final sortText = json['sortText'];
+    final filterText = json['filterText'];
+    final insertText = json['insertText'];
+    final insertTextFormat =
+        new InsertTextFormat.fromJson(json['insertTextFormat']);
+    final textEdit = new TextEdit.fromJson(json['textEdit']);
+    final additionalTextEdits = json['additionalTextEdits']
+        ?.map((item) => new TextEdit.fromJson(item))
+        ?.cast<TextEdit>()
+        ?.toList();
+    final commitCharacters =
+        json['commitCharacters']?.map((item) => item)?.cast<String>()?.toList();
+    final command = new Command.fromJson(json['command']);
+    final data = json['data'];
+    return new CompletionItem(
+        label,
+        kind,
+        detail,
+        documentation,
+        deprecated,
+        preselect,
+        sortText,
+        filterText,
+        insertText,
+        insertTextFormat,
+        textEdit,
+        additionalTextEdits,
+        commitCharacters,
+        command,
+        data);
+  }
 
   /// An optional array of additional text edits that are applied when selecting
   /// this completion. Edits must not overlap (including the same insert
@@ -646,14 +1176,75 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('label') || !map['label'] is String) {
+      return false;
+    }
+    const validFieldNames = [
+      'label',
+      'kind',
+      'detail',
+      'documentation',
+      'deprecated',
+      'preselect',
+      'sortText',
+      'filterText',
+      'insertText',
+      'insertTextFormat',
+      'textEdit',
+      'additionalTextEdits',
+      'commitCharacters',
+      'command',
+      'data'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// The kind of a completion entry.
 class CompletionItemKind {
   const CompletionItemKind._(this._value);
+  const CompletionItemKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+      case 8:
+      case 9:
+      case 10:
+      case 11:
+      case 12:
+      case 13:
+      case 14:
+      case 15:
+      case 16:
+      case 17:
+      case 18:
+      case 19:
+      case 20:
+      case 21:
+      case 22:
+      case 23:
+      case 24:
+      case 25:
+        return true;
+    }
+    return false;
+  }
+
   static const Text = const CompletionItemKind._(1);
   static const Method = const CompletionItemKind._(2);
   static const Function = const CompletionItemKind._(3);
@@ -694,7 +1285,22 @@
 /// Represents a collection of completion items ([CompletionItem]) to be
 /// presented in the editor.
 class CompletionList {
-  CompletionList(this.isIncomplete, this.items);
+  CompletionList(this.isIncomplete, this.items) {
+    if (isIncomplete == null) {
+      throw 'isIncomplete is required but was not provided';
+    }
+    if (items == null) {
+      throw 'items is required but was not provided';
+    }
+  }
+  factory CompletionList.fromJson(Map<String, dynamic> json) {
+    final isIncomplete = json['isIncomplete'];
+    final items = json['items']
+        ?.map((item) => new CompletionItem.fromJson(item))
+        ?.cast<CompletionItem>()
+        ?.toList();
+    return new CompletionList(isIncomplete, items);
+  }
 
   /// This list it not complete. Further typing should result in recomputing
   /// this list.
@@ -710,11 +1316,37 @@
     __result['items'] = items ?? (throw 'items is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('isIncomplete') || !map['isIncomplete'] is bool) {
+      return false;
+    }
+    if (!map.containsKey('items') ||
+        !(map['items'] is List &&
+            (map['items'].length == 0 ||
+                map['items'].every((item) => CompletionItem.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['isIncomplete', 'items'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Completion options.
 class CompletionOptions {
   CompletionOptions(this.resolveProvider, this.triggerCharacters);
+  factory CompletionOptions.fromJson(Map<String, dynamic> json) {
+    final resolveProvider = json['resolveProvider'];
+    final triggerCharacters = json['triggerCharacters']
+        ?.map((item) => item)
+        ?.cast<String>()
+        ?.toList();
+    return new CompletionOptions(resolveProvider, triggerCharacters);
+  }
 
   /// The server provides support to resolve additional information for a
   /// completion item.
@@ -733,14 +1365,37 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['resolveProvider', 'triggerCharacters'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class CompletionParams implements TextDocumentPositionParams {
-  CompletionParams(this.context, this.textDocument, this.position);
+  CompletionParams(this.context, this.textDocument, this.position) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (position == null) {
+      throw 'position is required but was not provided';
+    }
+  }
+  factory CompletionParams.fromJson(Map<String, dynamic> json) {
+    final context = new CompletionContext.fromJson(json['context']);
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final position = new Position.fromJson(json['position']);
+    return new CompletionParams(context, textDocument, position);
+  }
 
-  /// The completion context. This is only available if the client specifies to
-  /// send this using `ClientCapabilities.textDocument.completion.contextSupport
-  /// === true`
+  /// The completion context. This is only available if the client specifies
+  /// to send this using
+  /// `ClientCapabilities.textDocument.completion.contextSupport === true`
   final CompletionContext context;
 
   /// The position inside the text document.
@@ -760,11 +1415,40 @@
         position ?? (throw 'position is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('position') || !Position.canParse(map['position'])) {
+      return false;
+    }
+    const validFieldNames = ['context', 'textDocument', 'position'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class CompletionRegistrationOptions implements TextDocumentRegistrationOptions {
   CompletionRegistrationOptions(
       this.triggerCharacters, this.resolveProvider, this.documentSelector);
+  factory CompletionRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final triggerCharacters = json['triggerCharacters']
+        ?.map((item) => item)
+        ?.cast<String>()
+        ?.toList();
+    final resolveProvider = json['resolveProvider'];
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new CompletionRegistrationOptions(
+        triggerCharacters, resolveProvider, documentSelector);
+  }
 
   /// A document selector to identify the scope of the registration. If set to
   /// null the document selector provided on the client side will be used.
@@ -775,11 +1459,11 @@
   final bool resolveProvider;
 
   /// Most tools trigger completion request automatically without explicitly
-  /// requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they
-  /// do so when the user starts to type an identifier. For example if the user
-  /// types `c` in a JavaScript file code complete will automatically pop up
-  /// present `console` besides others as a completion item. Characters that
-  /// make up identifiers don't need to be listed here.
+  /// requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically
+  /// they do so when the user starts to type an identifier. For example if
+  /// the user types `c` in a JavaScript file code complete will automatically
+  /// pop up present `console` besides others as a completion item. Characters
+  /// that make up identifiers don't need to be listed here.
   ///
   /// If code complete should automatically be trigger on characters not being
   /// valid inside an identifier (for example `.` in JavaScript) list them in
@@ -797,14 +1481,45 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = [
+      'triggerCharacters',
+      'resolveProvider',
+      'documentSelector'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// How a completion was triggered
 class CompletionTriggerKind {
   const CompletionTriggerKind._(this._value);
+  const CompletionTriggerKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+        return true;
+    }
+    return false;
+  }
+
   /// Completion was triggered by typing an identifier (24x7 code complete),
   /// manual invocation (e.g Ctrl+Space) or via API.
   static const Invoked = const CompletionTriggerKind._(1);
@@ -813,7 +1528,8 @@
   /// `triggerCharacters` properties of the `CompletionRegistrationOptions`.
   static const TriggerCharacter = const CompletionTriggerKind._(2);
 
-  /// Completion was re-triggered as the current completion list is incomplete.
+  /// Completion was re-triggered as the current completion list is
+  /// incomplete.
   static const TriggerForIncompleteCompletions =
       const CompletionTriggerKind._(3);
 
@@ -830,6 +1546,11 @@
 
 class ConfigurationItem {
   ConfigurationItem(this.scopeUri, this.section);
+  factory ConfigurationItem.fromJson(Map<String, dynamic> json) {
+    final scopeUri = json['scopeUri'];
+    final section = json['section'];
+    return new ConfigurationItem(scopeUri, section);
+  }
 
   /// The scope to get the configuration section for.
   final String scopeUri;
@@ -847,10 +1568,30 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['scopeUri', 'section'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ConfigurationParams {
-  ConfigurationParams(this.items);
+  ConfigurationParams(this.items) {
+    if (items == null) {
+      throw 'items is required but was not provided';
+    }
+  }
+  factory ConfigurationParams.fromJson(Map<String, dynamic> json) {
+    final items = json['items']
+        ?.map((item) => new ConfigurationItem.fromJson(item))
+        ?.cast<ConfigurationItem>()
+        ?.toList();
+    return new ConfigurationParams(items);
+  }
 
   final List<ConfigurationItem> items;
 
@@ -859,11 +1600,36 @@
     __result['items'] = items ?? (throw 'items is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('items') ||
+        !(map['items'] is List &&
+            (map['items'].length == 0 ||
+                map['items']
+                    .every((item) => ConfigurationItem.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['items'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Create file operation
 class CreateFile implements FileOperation {
-  CreateFile(this.uri, this.options);
+  CreateFile(this.uri, this.options) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+  }
+  factory CreateFile.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    final options = new CreateFileOptions.fromJson(json['options']);
+    return new CreateFile(uri, options);
+  }
 
   /// Additional options
   final CreateFileOptions options;
@@ -879,11 +1645,28 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    const validFieldNames = ['uri', 'options'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Options to create a file.
 class CreateFileOptions {
   CreateFileOptions(this.overwrite, this.ignoreIfExists);
+  factory CreateFileOptions.fromJson(Map<String, dynamic> json) {
+    final overwrite = json['overwrite'];
+    final ignoreIfExists = json['ignoreIfExists'];
+    return new CreateFileOptions(overwrite, ignoreIfExists);
+  }
 
   /// Ignore if exists.
   final bool ignoreIfExists;
@@ -901,11 +1684,29 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['overwrite', 'ignoreIfExists'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Delete file operation
 class DeleteFile implements FileOperation {
-  DeleteFile(this.uri, this.options);
+  DeleteFile(this.uri, this.options) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+  }
+  factory DeleteFile.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    final options = new DeleteFileOptions.fromJson(json['options']);
+    return new DeleteFile(uri, options);
+  }
 
   /// Delete options.
   final DeleteFileOptions options;
@@ -921,11 +1722,28 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    const validFieldNames = ['uri', 'options'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Delete file options
 class DeleteFileOptions {
   DeleteFileOptions(this.recursive, this.ignoreIfNotExists);
+  factory DeleteFileOptions.fromJson(Map<String, dynamic> json) {
+    final recursive = json['recursive'];
+    final ignoreIfNotExists = json['ignoreIfNotExists'];
+    return new DeleteFileOptions(recursive, ignoreIfNotExists);
+  }
 
   /// Ignore the operation if the file doesn't exist.
   final bool ignoreIfNotExists;
@@ -943,11 +1761,44 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['recursive', 'ignoreIfNotExists'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class Diagnostic {
   Diagnostic(this.range, this.severity, this.code, this.source, this.message,
-      this.relatedInformation);
+      this.relatedInformation) {
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+    if (message == null) {
+      throw 'message is required but was not provided';
+    }
+  }
+  factory Diagnostic.fromJson(Map<String, dynamic> json) {
+    final range = new Range.fromJson(json['range']);
+    final severity = new DiagnosticSeverity.fromJson(json['severity']);
+    final code = json['code'] is num
+        ? new Either2<num, String>.t1(json['code'])
+        : (json['code'] is String
+            ? new Either2<num, String>.t2(json['code'])
+            : (throw '''${json['code']} was not one of (number, string)'''));
+    final source = json['source'];
+    final message = json['message'];
+    final relatedInformation = json['relatedInformation']
+        ?.map((item) => new DiagnosticRelatedInformation.fromJson(item))
+        ?.cast<DiagnosticRelatedInformation>()
+        ?.toList();
+    return new Diagnostic(
+        range, severity, code, source, message, relatedInformation);
+  }
 
   /// The diagnostic's code, which might appear in the user interface.
   final Either2<num, String> code;
@@ -958,8 +1809,9 @@
   /// The range at which the message applies.
   final Range range;
 
-  /// An array of related diagnostic information, e.g. when symbol-names within
-  /// a scope collide all definitions can be marked via this property.
+  /// An array of related diagnostic information, e.g. when symbol-names
+  /// within a scope collide all definitions can be marked via this
+  /// property.
   final List<DiagnosticRelatedInformation> relatedInformation;
 
   /// The diagnostic's severity. Can be omitted. If omitted it is up to the
@@ -989,13 +1841,47 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    if (!map.containsKey('message') || !map['message'] is String) {
+      return false;
+    }
+    const validFieldNames = [
+      'range',
+      'severity',
+      'code',
+      'source',
+      'message',
+      'relatedInformation'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Represents a related message and source code location for a diagnostic. This
-/// should be used to point to code locations that cause or related to a
-/// diagnostics, e.g when duplicating a symbol in a scope.
+/// Represents a related message and source code location for a diagnostic.
+/// This should be used to point to code locations that cause or related to
+/// a diagnostics, e.g when duplicating a symbol in a scope.
 class DiagnosticRelatedInformation {
-  DiagnosticRelatedInformation(this.location, this.message);
+  DiagnosticRelatedInformation(this.location, this.message) {
+    if (location == null) {
+      throw 'location is required but was not provided';
+    }
+    if (message == null) {
+      throw 'message is required but was not provided';
+    }
+  }
+  factory DiagnosticRelatedInformation.fromJson(Map<String, dynamic> json) {
+    final location = new Location.fromJson(json['location']);
+    final message = json['message'];
+    return new DiagnosticRelatedInformation(location, message);
+  }
 
   /// The location of this related diagnostic information.
   final Location location;
@@ -1011,13 +1897,40 @@
         message ?? (throw 'message is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('location') || !Location.canParse(map['location'])) {
+      return false;
+    }
+    if (!map.containsKey('message') || !map['message'] is String) {
+      return false;
+    }
+    const validFieldNames = ['location', 'message'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DiagnosticSeverity {
   const DiagnosticSeverity._(this._value);
+  const DiagnosticSeverity.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+        return true;
+    }
+    return false;
+  }
+
   /// Reports an error.
   static const Error = const DiagnosticSeverity._(1);
 
@@ -1042,7 +1955,15 @@
 }
 
 class DidChangeConfigurationParams {
-  DidChangeConfigurationParams(this.settings);
+  DidChangeConfigurationParams(this.settings) {
+    if (settings == null) {
+      throw 'settings is required but was not provided';
+    }
+  }
+  factory DidChangeConfigurationParams.fromJson(Map<String, dynamic> json) {
+    final settings = json['settings'];
+    return new DidChangeConfigurationParams(settings);
+  }
 
   /// The actual changed settings
   final dynamic settings;
@@ -1053,18 +1974,47 @@
         settings ?? (throw 'settings is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('settings') || !true) {
+      return false;
+    }
+    const validFieldNames = ['settings'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DidChangeTextDocumentParams {
-  DidChangeTextDocumentParams(this.textDocument, this.contentChanges);
+  DidChangeTextDocumentParams(this.textDocument, this.contentChanges) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (contentChanges == null) {
+      throw 'contentChanges is required but was not provided';
+    }
+  }
+  factory DidChangeTextDocumentParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new VersionedTextDocumentIdentifier.fromJson(json['textDocument']);
+    final contentChanges = json['contentChanges']
+        ?.map((item) => new TextDocumentContentChangeEvent.fromJson(item))
+        ?.cast<TextDocumentContentChangeEvent>()
+        ?.toList();
+    return new DidChangeTextDocumentParams(textDocument, contentChanges);
+  }
 
-  /// The actual content changes. The content changes describe single state
-  /// changes to the document. So if there are two content changes c1 and c2 for
-  /// a document in state S then c1 move the document to S' and c2 to S''.
+  /// The actual content changes. The content changes describe single
+  /// state changes to the document. So if there are two content changes
+  /// c1 and c2 for a document in state S then c1 move the document to S'
+  /// and c2 to S''.
   final List<TextDocumentContentChangeEvent> contentChanges;
 
-  /// The document that did change. The version number points to the version
-  /// after all provided content changes have been applied.
+  /// The document that did change. The version number points to the
+  /// version after all provided content changes have been applied.
   final VersionedTextDocumentIdentifier textDocument;
 
   Map<String, dynamic> toJson() {
@@ -1075,10 +2025,41 @@
         contentChanges ?? (throw 'contentChanges is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !VersionedTextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('contentChanges') ||
+        !(map['contentChanges'] is List &&
+            (map['contentChanges'].length == 0 ||
+                map['contentChanges'].every((item) =>
+                    TextDocumentContentChangeEvent.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'contentChanges'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DidChangeWatchedFilesParams {
-  DidChangeWatchedFilesParams(this.changes);
+  DidChangeWatchedFilesParams(this.changes) {
+    if (changes == null) {
+      throw 'changes is required but was not provided';
+    }
+  }
+  factory DidChangeWatchedFilesParams.fromJson(Map<String, dynamic> json) {
+    final changes = json['changes']
+        ?.map((item) => new FileEvent.fromJson(item))
+        ?.cast<FileEvent>()
+        ?.toList();
+    return new DidChangeWatchedFilesParams(changes);
+  }
 
   /// The actual file events.
   final List<FileEvent> changes;
@@ -1089,12 +2070,39 @@
         changes ?? (throw 'changes is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('changes') ||
+        !(map['changes'] is List &&
+            (map['changes'].length == 0 ||
+                map['changes'].every((item) => FileEvent.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['changes'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Describe options to be used when registering for text document change
 /// events.
 class DidChangeWatchedFilesRegistrationOptions {
-  DidChangeWatchedFilesRegistrationOptions(this.watchers);
+  DidChangeWatchedFilesRegistrationOptions(this.watchers) {
+    if (watchers == null) {
+      throw 'watchers is required but was not provided';
+    }
+  }
+  factory DidChangeWatchedFilesRegistrationOptions.fromJson(
+      Map<String, dynamic> json) {
+    final watchers = json['watchers']
+        ?.map((item) => new FileSystemWatcher.fromJson(item))
+        ?.cast<FileSystemWatcher>()
+        ?.toList();
+    return new DidChangeWatchedFilesRegistrationOptions(watchers);
+  }
 
   /// The watchers to register.
   final List<FileSystemWatcher> watchers;
@@ -1105,10 +2113,34 @@
         watchers ?? (throw 'watchers is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('watchers') ||
+        !(map['watchers'] is List &&
+            (map['watchers'].length == 0 ||
+                map['watchers']
+                    .every((item) => FileSystemWatcher.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['watchers'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DidChangeWorkspaceFoldersParams {
-  DidChangeWorkspaceFoldersParams(this.event);
+  DidChangeWorkspaceFoldersParams(this.event) {
+    if (event == null) {
+      throw 'event is required but was not provided';
+    }
+  }
+  factory DidChangeWorkspaceFoldersParams.fromJson(Map<String, dynamic> json) {
+    final event = new WorkspaceFoldersChangeEvent.fromJson(json['event']);
+    return new DidChangeWorkspaceFoldersParams(event);
+  }
 
   /// The actual workspace folder change event.
   final WorkspaceFoldersChangeEvent event;
@@ -1118,10 +2150,32 @@
     __result['event'] = event ?? (throw 'event is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('event') ||
+        !WorkspaceFoldersChangeEvent.canParse(map['event'])) {
+      return false;
+    }
+    const validFieldNames = ['event'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DidCloseTextDocumentParams {
-  DidCloseTextDocumentParams(this.textDocument);
+  DidCloseTextDocumentParams(this.textDocument) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+  }
+  factory DidCloseTextDocumentParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    return new DidCloseTextDocumentParams(textDocument);
+  }
 
   /// The document that was closed.
   final TextDocumentIdentifier textDocument;
@@ -1132,10 +2186,31 @@
         textDocument ?? (throw 'textDocument is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DidOpenTextDocumentParams {
-  DidOpenTextDocumentParams(this.textDocument);
+  DidOpenTextDocumentParams(this.textDocument) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+  }
+  factory DidOpenTextDocumentParams.fromJson(Map<String, dynamic> json) {
+    final textDocument = new TextDocumentItem.fromJson(json['textDocument']);
+    return new DidOpenTextDocumentParams(textDocument);
+  }
 
   /// The document that was opened.
   final TextDocumentItem textDocument;
@@ -1146,13 +2221,36 @@
         textDocument ?? (throw 'textDocument is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentItem.canParse(map['textDocument'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DidSaveTextDocumentParams {
-  DidSaveTextDocumentParams(this.textDocument, this.text);
+  DidSaveTextDocumentParams(this.textDocument, this.text) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+  }
+  factory DidSaveTextDocumentParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final text = json['text'];
+    return new DidSaveTextDocumentParams(textDocument, text);
+  }
 
-  /// Optional the content when saved. Depends on the includeText value when the
-  /// save notification was requested.
+  /// Optional the content when saved. Depends on the includeText value
+  /// when the save notification was requested.
   final String text;
 
   /// The document that was saved.
@@ -1167,10 +2265,29 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'text'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentFilter {
   DocumentFilter(this.language, this.scheme, this.pattern);
+  factory DocumentFilter.fromJson(Map<String, dynamic> json) {
+    final language = json['language'];
+    final scheme = json['scheme'];
+    final pattern = json['pattern'];
+    return new DocumentFilter(language, scheme, pattern);
+  }
 
   /// A language id, like `typescript`.
   final String language;
@@ -1194,10 +2311,32 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['language', 'scheme', 'pattern'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentFormattingParams {
-  DocumentFormattingParams(this.textDocument, this.options);
+  DocumentFormattingParams(this.textDocument, this.options) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (options == null) {
+      throw 'options is required but was not provided';
+    }
+  }
+  factory DocumentFormattingParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final options = new FormattingOptions.fromJson(json['options']);
+    return new DocumentFormattingParams(textDocument, options);
+  }
 
   /// The format options.
   final FormattingOptions options;
@@ -1213,13 +2352,39 @@
         options ?? (throw 'options is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('options') ||
+        !FormattingOptions.canParse(map['options'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'options'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// A document highlight is a range inside a text document which deserves
-/// special attention. Usually a document highlight is visualized by changing
-/// the background color of its range.
+/// special attention. Usually a document highlight is visualized by
+/// changing the background color of its range.
 class DocumentHighlight {
-  DocumentHighlight(this.range, this.kind);
+  DocumentHighlight(this.range, this.kind) {
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+  }
+  factory DocumentHighlight.fromJson(Map<String, dynamic> json) {
+    final range = new Range.fromJson(json['range']);
+    final kind = new DocumentHighlightKind.fromJson(json['kind']);
+    return new DocumentHighlight(range, kind);
+  }
 
   /// The highlight kind, default is DocumentHighlightKind.Text.
   final DocumentHighlightKind kind;
@@ -1235,14 +2400,37 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    const validFieldNames = ['range', 'kind'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// A document highlight kind.
 class DocumentHighlightKind {
   const DocumentHighlightKind._(this._value);
+  const DocumentHighlightKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+        return true;
+    }
+    return false;
+  }
+
   /// A textual occurrence.
   static const Text = const DocumentHighlightKind._(1);
 
@@ -1263,10 +2451,21 @@
   bool operator ==(o) => o is DocumentHighlightKind && o._value == _value;
 }
 
-/// A document link is a range in a text document that links to an internal or
-/// external resource, like another text document or a web site.
+/// A document link is a range in a text document that links to an
+/// internal or external resource, like another text document or a web
+/// site.
 class DocumentLink {
-  DocumentLink(this.range, this.target, this.data);
+  DocumentLink(this.range, this.target, this.data) {
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+  }
+  factory DocumentLink.fromJson(Map<String, dynamic> json) {
+    final range = new Range.fromJson(json['range']);
+    final target = json['target'];
+    final data = json['data'];
+    return new DocumentLink(range, target, data);
+  }
 
   /// A data entry field that is preserved on a document link between a
   /// DocumentLinkRequest and a DocumentLinkResolveRequest.
@@ -1275,7 +2474,8 @@
   /// The range this link applies to.
   final Range range;
 
-  /// The uri this link points to. If missing a resolve request is sent later.
+  /// The uri this link points to. If missing a resolve request is sent
+  /// later.
   final String target;
 
   Map<String, dynamic> toJson() {
@@ -1289,11 +2489,27 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    const validFieldNames = ['range', 'target', 'data'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Document link options.
 class DocumentLinkOptions {
   DocumentLinkOptions(this.resolveProvider);
+  factory DocumentLinkOptions.fromJson(Map<String, dynamic> json) {
+    final resolveProvider = json['resolveProvider'];
+    return new DocumentLinkOptions(resolveProvider);
+  }
 
   /// Document links have a resolve provider as well.
   final bool resolveProvider;
@@ -1305,10 +2521,28 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['resolveProvider'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentLinkParams {
-  DocumentLinkParams(this.textDocument);
+  DocumentLinkParams(this.textDocument) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+  }
+  factory DocumentLinkParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    return new DocumentLinkParams(textDocument);
+  }
 
   /// The document to provide document links for.
   final TextDocumentIdentifier textDocument;
@@ -1319,14 +2553,37 @@
         textDocument ?? (throw 'textDocument is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentLinkRegistrationOptions
     implements TextDocumentRegistrationOptions {
   DocumentLinkRegistrationOptions(this.resolveProvider, this.documentSelector);
+  factory DocumentLinkRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final resolveProvider = json['resolveProvider'];
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new DocumentLinkRegistrationOptions(
+        resolveProvider, documentSelector);
+  }
 
-  /// A document selector to identify the scope of the registration. If set to
-  /// null the document selector provided on the client side will be used.
+  /// A document selector to identify the scope of the registration. If
+  /// set to null the document selector provided on the client side will
+  /// be used.
   final List<DocumentFilter> documentSelector;
 
   /// Document links have a resolve provider as well.
@@ -1340,12 +2597,41 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['resolveProvider', 'documentSelector'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Format document on type options.
 class DocumentOnTypeFormattingOptions {
   DocumentOnTypeFormattingOptions(
-      this.firstTriggerCharacter, this.moreTriggerCharacter);
+      this.firstTriggerCharacter, this.moreTriggerCharacter) {
+    if (firstTriggerCharacter == null) {
+      throw 'firstTriggerCharacter is required but was not provided';
+    }
+  }
+  factory DocumentOnTypeFormattingOptions.fromJson(Map<String, dynamic> json) {
+    final firstTriggerCharacter = json['firstTriggerCharacter'];
+    final moreTriggerCharacter = json['moreTriggerCharacter']
+        ?.map((item) => item)
+        ?.cast<String>()
+        ?.toList();
+    return new DocumentOnTypeFormattingOptions(
+        firstTriggerCharacter, moreTriggerCharacter);
+  }
 
   /// A character on which formatting should be triggered, like `}`.
   final String firstTriggerCharacter;
@@ -1362,11 +2648,46 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('firstTriggerCharacter') ||
+        !map['firstTriggerCharacter'] is String) {
+      return false;
+    }
+    const validFieldNames = ['firstTriggerCharacter', 'moreTriggerCharacter'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentOnTypeFormattingParams {
   DocumentOnTypeFormattingParams(
-      this.textDocument, this.position, this.ch, this.options);
+      this.textDocument, this.position, this.ch, this.options) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (position == null) {
+      throw 'position is required but was not provided';
+    }
+    if (ch == null) {
+      throw 'ch is required but was not provided';
+    }
+    if (options == null) {
+      throw 'options is required but was not provided';
+    }
+  }
+  factory DocumentOnTypeFormattingParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final position = new Position.fromJson(json['position']);
+    final ch = json['ch'];
+    final options = new FormattingOptions.fromJson(json['options']);
+    return new DocumentOnTypeFormattingParams(
+        textDocument, position, ch, options);
+  }
 
   /// The character that has been typed.
   final String ch;
@@ -1391,15 +2712,57 @@
         options ?? (throw 'options is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('position') || !Position.canParse(map['position'])) {
+      return false;
+    }
+    if (!map.containsKey('ch') || !map['ch'] is String) {
+      return false;
+    }
+    if (!map.containsKey('options') ||
+        !FormattingOptions.canParse(map['options'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'position', 'ch', 'options'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentOnTypeFormattingRegistrationOptions
     implements TextDocumentRegistrationOptions {
   DocumentOnTypeFormattingRegistrationOptions(this.firstTriggerCharacter,
-      this.moreTriggerCharacter, this.documentSelector);
+      this.moreTriggerCharacter, this.documentSelector) {
+    if (firstTriggerCharacter == null) {
+      throw 'firstTriggerCharacter is required but was not provided';
+    }
+  }
+  factory DocumentOnTypeFormattingRegistrationOptions.fromJson(
+      Map<String, dynamic> json) {
+    final firstTriggerCharacter = json['firstTriggerCharacter'];
+    final moreTriggerCharacter = json['moreTriggerCharacter']
+        ?.map((item) => item)
+        ?.cast<String>()
+        ?.toList();
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new DocumentOnTypeFormattingRegistrationOptions(
+        firstTriggerCharacter, moreTriggerCharacter, documentSelector);
+  }
 
-  /// A document selector to identify the scope of the registration. If set to
-  /// null the document selector provided on the client side will be used.
+  /// A document selector to identify the scope of the registration. If
+  /// set to null the document selector provided on the client side will
+  /// be used.
   final List<DocumentFilter> documentSelector;
 
   /// A character on which formatting should be triggered, like `}`.
@@ -1418,10 +2781,51 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('firstTriggerCharacter') ||
+        !map['firstTriggerCharacter'] is String) {
+      return false;
+    }
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = [
+      'firstTriggerCharacter',
+      'moreTriggerCharacter',
+      'documentSelector'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentRangeFormattingParams {
-  DocumentRangeFormattingParams(this.textDocument, this.range, this.options);
+  DocumentRangeFormattingParams(this.textDocument, this.range, this.options) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+    if (options == null) {
+      throw 'options is required but was not provided';
+    }
+  }
+  factory DocumentRangeFormattingParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final range = new Range.fromJson(json['range']);
+    final options = new FormattingOptions.fromJson(json['options']);
+    return new DocumentRangeFormattingParams(textDocument, range, options);
+  }
 
   /// The format options
   final FormattingOptions options;
@@ -1441,15 +2845,63 @@
         options ?? (throw 'options is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    if (!map.containsKey('options') ||
+        !FormattingOptions.canParse(map['options'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'range', 'options'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Represents programming constructs like variables, classes, interfaces etc.
-/// that appear in a document. Document symbols can be hierarchical and they
-/// have two ranges: one that encloses its definition and one that points to its
-/// most interesting range, e.g. the range of an identifier.
+/// Represents programming constructs like variables, classes,
+/// interfaces etc. that appear in a document. Document symbols can be
+/// hierarchical and they have two ranges: one that encloses its
+/// definition and one that points to its most interesting range, e.g.
+/// the range of an identifier.
 class DocumentSymbol {
   DocumentSymbol(this.name, this.detail, this.kind, this.deprecated, this.range,
-      this.selectionRange, this.children);
+      this.selectionRange, this.children) {
+    if (name == null) {
+      throw 'name is required but was not provided';
+    }
+    if (kind == null) {
+      throw 'kind is required but was not provided';
+    }
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+    if (selectionRange == null) {
+      throw 'selectionRange is required but was not provided';
+    }
+  }
+  factory DocumentSymbol.fromJson(Map<String, dynamic> json) {
+    final name = json['name'];
+    final detail = json['detail'];
+    final kind = new SymbolKind.fromJson(json['kind']);
+    final deprecated = json['deprecated'];
+    final range = new Range.fromJson(json['range']);
+    final selectionRange = new Range.fromJson(json['selectionRange']);
+    final children = json['children']
+        ?.map((item) => new DocumentSymbol.fromJson(item))
+        ?.cast<DocumentSymbol>()
+        ?.toList();
+    return new DocumentSymbol(
+        name, detail, kind, deprecated, range, selectionRange, children);
+  }
 
   /// Children of this symbol, e.g. properties of a class.
   final List<DocumentSymbol> children;
@@ -1466,14 +2918,15 @@
   /// The name of this symbol.
   final String name;
 
-  /// The range enclosing this symbol not including leading/trailing whitespace
-  /// but everything else like comments. This information is typically used to
-  /// determine if the clients cursor is inside the symbol to reveal in the
-  /// symbol in the UI.
+  /// The range enclosing this symbol not including leading/trailing
+  /// whitespace but everything else like comments. This information is
+  /// typically used to determine if the clients cursor is inside the
+  /// symbol to reveal in the symbol in the UI.
   final Range range;
 
-  /// The range that should be selected and revealed when this symbol is being
-  /// picked, e.g the name of a function. Must be contained by the `range`.
+  /// The range that should be selected and revealed when this symbol is
+  /// being picked, e.g the name of a function. Must be contained by the
+  /// `range`.
   final Range selectionRange;
 
   Map<String, dynamic> toJson() {
@@ -1494,10 +2947,49 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('name') || !map['name'] is String) {
+      return false;
+    }
+    if (!map.containsKey('kind') || !SymbolKind.canParse(map['kind'])) {
+      return false;
+    }
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    if (!map.containsKey('selectionRange') ||
+        !Range.canParse(map['selectionRange'])) {
+      return false;
+    }
+    const validFieldNames = [
+      'name',
+      'detail',
+      'kind',
+      'deprecated',
+      'range',
+      'selectionRange',
+      'children'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class DocumentSymbolParams {
-  DocumentSymbolParams(this.textDocument);
+  DocumentSymbolParams(this.textDocument) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+  }
+  factory DocumentSymbolParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    return new DocumentSymbolParams(textDocument);
+  }
 
   /// The text document.
   final TextDocumentIdentifier textDocument;
@@ -1508,6 +3000,19 @@
         textDocument ?? (throw 'textDocument is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 abstract class ErrorCodes {
@@ -1525,7 +3030,16 @@
 
 /// Execute command options.
 class ExecuteCommandOptions {
-  ExecuteCommandOptions(this.commands);
+  ExecuteCommandOptions(this.commands) {
+    if (commands == null) {
+      throw 'commands is required but was not provided';
+    }
+  }
+  factory ExecuteCommandOptions.fromJson(Map<String, dynamic> json) {
+    final commands =
+        json['commands']?.map((item) => item)?.cast<String>()?.toList();
+    return new ExecuteCommandOptions(commands);
+  }
 
   /// The commands to be executed on the server
   final List<String> commands;
@@ -1536,10 +3050,35 @@
         commands ?? (throw 'commands is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('commands') ||
+        !(map['commands'] is List &&
+            (map['commands'].length == 0 ||
+                map['commands'].every((item) => item is String)))) {
+      return false;
+    }
+    const validFieldNames = ['commands'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ExecuteCommandParams {
-  ExecuteCommandParams(this.command, this.arguments);
+  ExecuteCommandParams(this.command, this.arguments) {
+    if (command == null) {
+      throw 'command is required but was not provided';
+    }
+  }
+  factory ExecuteCommandParams.fromJson(Map<String, dynamic> json) {
+    final command = json['command'];
+    final arguments =
+        json['arguments']?.map((item) => item)?.cast<dynamic>()?.toList();
+    return new ExecuteCommandParams(command, arguments);
+  }
 
   /// Arguments that the command should be invoked with.
   final List<dynamic> arguments;
@@ -1556,11 +3095,33 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('command') || !map['command'] is String) {
+      return false;
+    }
+    const validFieldNames = ['command', 'arguments'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Execute command registration options.
 class ExecuteCommandRegistrationOptions {
-  ExecuteCommandRegistrationOptions(this.commands);
+  ExecuteCommandRegistrationOptions(this.commands) {
+    if (commands == null) {
+      throw 'commands is required but was not provided';
+    }
+  }
+  factory ExecuteCommandRegistrationOptions.fromJson(
+      Map<String, dynamic> json) {
+    final commands =
+        json['commands']?.map((item) => item)?.cast<String>()?.toList();
+    return new ExecuteCommandRegistrationOptions(commands);
+  }
 
   /// The commands to be executed on the server
   final List<String> commands;
@@ -1571,30 +3132,59 @@
         commands ?? (throw 'commands is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('commands') ||
+        !(map['commands'] is List &&
+            (map['commands'].length == 0 ||
+                map['commands'].every((item) => item is String)))) {
+      return false;
+    }
+    const validFieldNames = ['commands'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class FailureHandlingKind {
   const FailureHandlingKind._(this._value);
+  const FailureHandlingKind.fromJson(this._value);
 
   final Object _value;
 
-  /// Applying the workspace change is simply aborted if one of the changes
-  /// provided fails. All operations executed before the failing operation stay
-  /// executed.
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 'abort':
+      case 'transactional':
+      case 'textOnlyTransactional':
+      case 'undo':
+        return true;
+    }
+    return false;
+  }
+
+  /// Applying the workspace change is simply aborted if one of the
+  /// changes provided fails. All operations executed before the
+  /// failing operation stay executed.
   static const Abort = const FailureHandlingKind._('abort');
 
-  /// All operations are executed transactional. That means they either all
-  /// succeed or no changes at all are applied to the workspace.
+  /// All operations are executed transactional. That means they
+  /// either all succeed or no changes at all are applied to the
+  /// workspace.
   static const Transactional = const FailureHandlingKind._('transactional');
 
-  /// If the workspace edit contains only textual file changes they are executed
-  /// transactional. If resource changes (create, rename or delete file) are
-  /// part of the change the failure handling startegy is abort.
+  /// If the workspace edit contains only textual file changes they
+  /// are executed transactional. If resource changes (create, rename
+  /// or delete file) are part of the change the failure handling
+  /// startegy is abort.
   static const TextOnlyTransactional =
       const FailureHandlingKind._('textOnlyTransactional');
 
-  /// The client tries to undo the operations already executed. But there is no
-  /// guaruntee that this is succeeding.
+  /// The client tries to undo the operations already executed. But
+  /// there is no guaruntee that this is succeeding.
   static const Undo = const FailureHandlingKind._('undo');
 
   Object toJson() => _value;
@@ -1611,9 +3201,20 @@
 /// The file event type.
 class FileChangeType {
   const FileChangeType._(this._value);
+  const FileChangeType.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+        return true;
+    }
+    return false;
+  }
+
   /// The file got created.
   static const Created = const FileChangeType._(1);
 
@@ -1636,7 +3237,19 @@
 
 /// An event describing a file change.
 class FileEvent {
-  FileEvent(this.uri, this.type);
+  FileEvent(this.uri, this.type) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+    if (type == null) {
+      throw 'type is required but was not provided';
+    }
+  }
+  factory FileEvent.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    final type = json['type'];
+    return new FileEvent(uri, type);
+  }
 
   /// The change type.
   final num type;
@@ -1650,16 +3263,41 @@
     __result['type'] = type ?? (throw 'type is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    if (!map.containsKey('type') || !map['type'] is num) {
+      return false;
+    }
+    const validFieldNames = ['uri', 'type'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class FileSystemWatcher {
-  FileSystemWatcher(this.globPattern, this.kind);
+  FileSystemWatcher(this.globPattern, this.kind) {
+    if (globPattern == null) {
+      throw 'globPattern is required but was not provided';
+    }
+  }
+  factory FileSystemWatcher.fromJson(Map<String, dynamic> json) {
+    final globPattern = json['globPattern'];
+    final kind = new WatchKind.fromJson(json['kind']);
+    return new FileSystemWatcher(globPattern, kind);
+  }
 
   /// The  glob pattern to watch
   final String globPattern;
 
-  /// The kind of events of interest. If omitted it defaults to WatchKind.Create
-  /// | WatchKind.Change | WatchKind.Delete which is 7.
+  /// The kind of events of interest. If omitted it defaults to
+  /// WatchKind.Create | WatchKind.Change | WatchKind.Delete which
+  /// is 7.
   final WatchKind kind;
 
   Map<String, dynamic> toJson() {
@@ -1671,28 +3309,57 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('globPattern') || !map['globPattern'] is String) {
+      return false;
+    }
+    const validFieldNames = ['globPattern', 'kind'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Represents a folding range.
 class FoldingRange {
   FoldingRange(this.startLine, this.startCharacter, this.endLine,
-      this.endCharacter, this.kind);
+      this.endCharacter, this.kind) {
+    if (startLine == null) {
+      throw 'startLine is required but was not provided';
+    }
+    if (endLine == null) {
+      throw 'endLine is required but was not provided';
+    }
+  }
+  factory FoldingRange.fromJson(Map<String, dynamic> json) {
+    final startLine = json['startLine'];
+    final startCharacter = json['startCharacter'];
+    final endLine = json['endLine'];
+    final endCharacter = json['endCharacter'];
+    final kind = new FoldingRangeKind.fromJson(json['kind']);
+    return new FoldingRange(
+        startLine, startCharacter, endLine, endCharacter, kind);
+  }
 
-  /// The zero-based character offset before the folded range ends. If not
-  /// defined, defaults to the length of the end line.
+  /// The zero-based character offset before the folded range ends.
+  /// If not defined, defaults to the length of the end line.
   final num endCharacter;
 
   /// The zero-based line number where the folded range ends.
   final num endLine;
 
-  /// Describes the kind of the folding range such as `comment' or 'region'. The
-  /// kind is used to categorize folding ranges and used by commands like 'Fold
-  /// all comments'. See [FoldingRangeKind] for an enumeration of standardized
-  /// kinds.
+  /// Describes the kind of the folding range such as `comment' or
+  /// 'region'. The kind is used to categorize folding ranges and
+  /// used by commands like 'Fold all comments'. See
+  /// [FoldingRangeKind] for an enumeration of standardized kinds.
   final FoldingRangeKind kind;
 
-  /// The zero-based character offset from where the folded range starts. If not
-  /// defined, defaults to the length of the start line.
+  /// The zero-based character offset from where the folded range
+  /// starts. If not defined, defaults to the length of the start
+  /// line.
   final num startCharacter;
 
   /// The zero-based line number from where the folded range starts.
@@ -1715,14 +3382,46 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('startLine') || !map['startLine'] is num) {
+      return false;
+    }
+    if (!map.containsKey('endLine') || !map['endLine'] is num) {
+      return false;
+    }
+    const validFieldNames = [
+      'startLine',
+      'startCharacter',
+      'endLine',
+      'endCharacter',
+      'kind'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Enum of known range kinds
 class FoldingRangeKind {
   const FoldingRangeKind._(this._value);
+  const FoldingRangeKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 'comment':
+      case 'imports':
+      case 'region':
+        return true;
+    }
+    return false;
+  }
+
   /// Folding range for a comment
   static const Comment = const FoldingRangeKind._('comment');
 
@@ -1744,7 +3443,16 @@
 }
 
 class FoldingRangeParams {
-  FoldingRangeParams(this.textDocument);
+  FoldingRangeParams(this.textDocument) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+  }
+  factory FoldingRangeParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    return new FoldingRangeParams(textDocument);
+  }
 
   /// The text document.
   final TextDocumentIdentifier textDocument;
@@ -1755,6 +3463,19 @@
         textDocument ?? (throw 'textDocument is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Folding range provider options.
@@ -1763,11 +3484,32 @@
     Map<String, dynamic> __result = {};
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = [''];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Value-object describing what options formatting should use.
 class FormattingOptions {
-  FormattingOptions(this.tabSize, this.insertSpaces);
+  FormattingOptions(this.tabSize, this.insertSpaces) {
+    if (tabSize == null) {
+      throw 'tabSize is required but was not provided';
+    }
+    if (insertSpaces == null) {
+      throw 'insertSpaces is required but was not provided';
+    }
+  }
+  factory FormattingOptions.fromJson(Map<String, dynamic> json) {
+    final tabSize = json['tabSize'];
+    final insertSpaces = json['insertSpaces'];
+    return new FormattingOptions(tabSize, insertSpaces);
+  }
 
   /// Prefer spaces over tabs.
   final bool insertSpaces;
@@ -1783,17 +3525,58 @@
         insertSpaces ?? (throw 'insertSpaces is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('tabSize') || !map['tabSize'] is num) {
+      return false;
+    }
+    if (!map.containsKey('insertSpaces') || !map['insertSpaces'] is bool) {
+      return false;
+    }
+    const validFieldNames = ['tabSize', 'insertSpaces'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// The result of a hover request.
 class Hover {
-  Hover(this.contents, this.range);
+  Hover(this.contents, this.range) {
+    if (contents == null) {
+      throw 'contents is required but was not provided';
+    }
+  }
+  factory Hover.fromJson(Map<String, dynamic> json) {
+    final contents = MarkedString.canParse(json['contents'])
+        ? new Either3<MarkedString, List<MarkedString>, MarkupContent>.t1(
+            new MarkedString.fromJson(json['contents']))
+        : ((json['contents'] is List &&
+                (json['contents'].length == 0 ||
+                    json['contents']
+                        .every((item) => MarkedString.canParse(item))))
+            ? new Either3<MarkedString, List<MarkedString>, MarkupContent>.t2(
+                json['contents']
+                    ?.map((item) => new MarkedString.fromJson(item))
+                    ?.cast<MarkedString>()
+                    ?.toList())
+            : (MarkupContent.canParse(json['contents'])
+                ? new Either3<MarkedString, List<MarkedString>,
+                        MarkupContent>.t3(
+                    new MarkupContent.fromJson(json['contents']))
+                : (throw '''${json['contents']} was not one of (MarkedString, MarkedString[], MarkupContent)''')));
+    final range = new Range.fromJson(json['range']);
+    return new Hover(contents, range);
+  }
 
   /// The hover's content
   final Either3<MarkedString, List<MarkedString>, MarkupContent> contents;
 
-  /// An optional range is a range inside a text document that is used to
-  /// visualize a hover, e.g. by changing the background color.
+  /// An optional range is a range inside a text document that is
+  /// used to visualize a hover, e.g. by changing the background
+  /// color.
   final Range range;
 
   Map<String, dynamic> toJson() {
@@ -1805,11 +3588,46 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('contents') ||
+        !(MarkedString.canParse(map['contents']) ||
+            (map['contents'] is List &&
+                (map['contents'].length == 0 ||
+                    map['contents']
+                        .every((item) => MarkedString.canParse(item)))) ||
+            MarkupContent.canParse(map['contents']))) {
+      return false;
+    }
+    const validFieldNames = ['contents', 'range'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class InitializeParams {
   InitializeParams(this.processId, this.rootPath, this.rootUri,
-      this.initializationOptions, this.capabilities, this.workspaceFolders);
+      this.initializationOptions, this.capabilities, this.workspaceFolders) {
+    if (capabilities == null) {
+      throw 'capabilities is required but was not provided';
+    }
+  }
+  factory InitializeParams.fromJson(Map<String, dynamic> json) {
+    final processId = json['processId'];
+    final rootPath = json['rootPath'];
+    final rootUri = json['rootUri'];
+    final initializationOptions = json['initializationOptions'];
+    final capabilities = new ClientCapabilities.fromJson(json['capabilities']);
+    final workspaceFolders = json['workspaceFolders']
+        ?.map((item) => new WorkspaceFolder.fromJson(item))
+        ?.cast<WorkspaceFolder>()
+        ?.toList();
+    return new InitializeParams(processId, rootPath, rootUri,
+        initializationOptions, capabilities, workspaceFolders);
+  }
 
   /// The capabilities provided by the client (editor or tool)
   final ClientCapabilities capabilities;
@@ -1817,10 +3635,10 @@
   /// User provided initialization options.
   final dynamic initializationOptions;
 
-  /// The process Id of the parent process that started the server. Is null if
-  /// the process has not been started by another process. If the parent process
-  /// is not alive then the server should exit (see exit notification) its
-  /// process.
+  /// The process Id of the parent process that started the
+  /// server. Is null if the process has not been started by
+  /// another process. If the parent process is not alive then the
+  /// server should exit (see exit notification) its process.
   final num processId;
 
   /// The rootPath of the workspace. Is null if no folder is open.
@@ -1828,14 +3646,14 @@
   @core.deprecated
   final String rootPath;
 
-  /// The rootUri of the workspace. Is null if no folder is open. If both
-  /// `rootPath` and `rootUri` are set `rootUri` wins.
+  /// The rootUri of the workspace. Is null if no folder is open.
+  /// If both `rootPath` and `rootUri` are set `rootUri` wins.
   final String rootUri;
 
-  /// The workspace folders configured in the client when the server starts.
-  /// This property is only available if the client supports workspace folders.
-  /// It can be `null` if the client supports workspace folders but none are
-  /// configured.
+  /// The workspace folders configured in the client when the
+  /// server starts. This property is only available if the client
+  /// supports workspace folders. It can be `null` if the client
+  /// supports workspace folders but none are configured.
   ///
   /// Since 3.6.0
   final List<WorkspaceFolder> workspaceFolders;
@@ -1859,10 +3677,44 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('processId') || !map['processId'] is num) {
+      return false;
+    }
+    if (!map.containsKey('rootUri') || !map['rootUri'] is String) {
+      return false;
+    }
+    if (!map.containsKey('capabilities') ||
+        !ClientCapabilities.canParse(map['capabilities'])) {
+      return false;
+    }
+    const validFieldNames = [
+      'processId',
+      'rootPath',
+      'rootUri',
+      'initializationOptions',
+      'capabilities',
+      'workspaceFolders'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class InitializeResult {
-  InitializeResult(this.capabilities);
+  InitializeResult(this.capabilities) {
+    if (capabilities == null) {
+      throw 'capabilities is required but was not provided';
+    }
+  }
+  factory InitializeResult.fromJson(Map<String, dynamic> json) {
+    final capabilities = new ServerCapabilities.fromJson(json['capabilities']);
+    return new InitializeResult(capabilities);
+  }
 
   /// The capabilities the language server provides.
   final ServerCapabilities capabilities;
@@ -1873,6 +3725,19 @@
         capabilities ?? (throw 'capabilities is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('capabilities') ||
+        !ServerCapabilities.canParse(map['capabilities'])) {
+      return false;
+    }
+    const validFieldNames = ['capabilities'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class InitializedParams {
@@ -1880,24 +3745,45 @@
     Map<String, dynamic> __result = {};
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = [''];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Defines whether the insert text in a completion item should be interpreted
-/// as plain text or a snippet.
+/// Defines whether the insert text in a completion item should be
+/// interpreted as plain text or a snippet.
 class InsertTextFormat {
   const InsertTextFormat._(this._value);
+  const InsertTextFormat.fromJson(this._value);
 
   final Object _value;
 
-  /// The primary text to be inserted is treated as a plain string.
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+        return true;
+    }
+    return false;
+  }
+
+  /// The primary text to be inserted is treated as a plain
+  /// string.
   static const PlainText = const InsertTextFormat._(1);
 
   /// The primary text to be inserted is treated as a snippet.
   ///
-  /// A snippet can define tab stops and placeholders with `$1`, `$2` and
-  /// `${3:foo}`. `$0` defines the final tab stop, it defaults to the end of the
-  /// snippet. Placeholders with equal identifiers are linked, that is typing in
-  /// one will update others too.
+  /// A snippet can define tab stops and placeholders with `$1`,
+  /// `$2` and `${3:foo}`. `$0` defines the final tab stop, it
+  /// defaults to the end of the snippet. Placeholders with
+  /// equal identifiers are linked, that is typing in one will
+  /// update others too.
   static const Snippet = const InsertTextFormat._(2);
 
   Object toJson() => _value;
@@ -1912,7 +3798,19 @@
 }
 
 class Location {
-  Location(this.uri, this.range);
+  Location(this.uri, this.range) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+  }
+  factory Location.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    final range = new Range.fromJson(json['range']);
+    return new Location(uri, range);
+  }
 
   final Range range;
   final String uri;
@@ -1923,10 +3821,37 @@
     __result['range'] = range ?? (throw 'range is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    const validFieldNames = ['uri', 'range'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class LogMessageParams {
-  LogMessageParams(this.type, this.message);
+  LogMessageParams(this.type, this.message) {
+    if (type == null) {
+      throw 'type is required but was not provided';
+    }
+    if (message == null) {
+      throw 'message is required but was not provided';
+    }
+  }
+  factory LogMessageParams.fromJson(Map<String, dynamic> json) {
+    final type = new MessageType.fromJson(json['type']);
+    final message = json['message'];
+    return new LogMessageParams(type, message);
+  }
 
   /// The actual message
   final String message;
@@ -1941,10 +3866,37 @@
         message ?? (throw 'message is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('type') || !MessageType.canParse(map['type'])) {
+      return false;
+    }
+    if (!map.containsKey('message') || !map['message'] is String) {
+      return false;
+    }
+    const validFieldNames = ['type', 'message'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class MarkedString {
-  MarkedString(this.language, this.value);
+  MarkedString(this.language, this.value) {
+    if (language == null) {
+      throw 'language is required but was not provided';
+    }
+    if (value == null) {
+      throw 'value is required but was not provided';
+    }
+  }
+  factory MarkedString.fromJson(Map<String, dynamic> json) {
+    final language = json['language'];
+    final value = json['value'];
+    return new MarkedString(language, value);
+  }
 
   final String language;
   final String value;
@@ -1956,18 +3908,35 @@
     __result['value'] = value ?? (throw 'value is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('language') || !map['language'] is String) {
+      return false;
+    }
+    if (!map.containsKey('value') || !map['value'] is String) {
+      return false;
+    }
+    const validFieldNames = ['language', 'value'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// A `MarkupContent` literal represents a string value which content is
-/// interpreted base on its kind flag. Currently the protocol supports
-/// `plaintext` and `markdown` as markup kinds.
+/// A `MarkupContent` literal represents a string value which
+/// content is interpreted base on its kind flag. Currently the
+/// protocol supports `plaintext` and `markdown` as markup
+/// kinds.
 ///
-/// If the kind is `markdown` then the value can contain fenced code blocks like
-/// in GitHub issues. See
+/// If the kind is `markdown` then the value can contain fenced
+/// code blocks like in GitHub issues. See
 /// https://help.github.com/articles/creating-and-highlighting-code-blocks/#syntax-highlighting
 ///
-/// Here is an example how such a string can be constructed using JavaScript /
-/// TypeScript: ```ts let markdown: MarkdownContent = {
+/// Here is an example how such a string can be constructed
+/// using JavaScript / TypeScript: ```ts let markdown:
+/// MarkdownContent = {
 ///
 /// kind: MarkupKind.Markdown,
 /// 	value: [
@@ -1978,10 +3947,23 @@
 /// 		'```'
 /// 	].join('\n') }; ```
 ///
-/// *Please Note* that clients might sanitize the return markdown. A client
-/// could decide to remove HTML from the markdown to avoid script execution.
+/// *Please Note* that clients might sanitize the return
+/// markdown. A client could decide to remove HTML from the
+/// markdown to avoid script execution.
 class MarkupContent {
-  MarkupContent(this.kind, this.value);
+  MarkupContent(this.kind, this.value) {
+    if (kind == null) {
+      throw 'kind is required but was not provided';
+    }
+    if (value == null) {
+      throw 'value is required but was not provided';
+    }
+  }
+  factory MarkupContent.fromJson(Map<String, dynamic> json) {
+    final kind = new MarkupKind.fromJson(json['kind']);
+    final value = json['value'];
+    return new MarkupContent(kind, value);
+  }
 
   /// The type of the Markup
   final MarkupKind kind;
@@ -1995,18 +3977,44 @@
     __result['value'] = value ?? (throw 'value is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('kind') || !MarkupKind.canParse(map['kind'])) {
+      return false;
+    }
+    if (!map.containsKey('value') || !map['value'] is String) {
+      return false;
+    }
+    const validFieldNames = ['kind', 'value'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Describes the content type that a client supports in various result literals
-/// like `Hover`, `ParameterInfo` or `CompletionItem`.
+/// Describes the content type that a client supports in various
+/// result literals like `Hover`, `ParameterInfo` or
+/// `CompletionItem`.
 ///
-/// Please note that `MarkupKinds` must not start with a `$`. This kinds are
-/// reserved for internal usage.
+/// Please note that `MarkupKinds` must not start with a `$`.
+/// This kinds are reserved for internal usage.
 class MarkupKind {
   const MarkupKind._(this._value);
+  const MarkupKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 'plaintext':
+      case 'markdown':
+        return true;
+    }
+    return false;
+  }
+
   /// Plain text is supported as a content format
   static const PlainText = const MarkupKind._('plaintext');
 
@@ -2025,7 +4033,15 @@
 }
 
 class Message {
-  Message(this.jsonrpc);
+  Message(this.jsonrpc) {
+    if (jsonrpc == null) {
+      throw 'jsonrpc is required but was not provided';
+    }
+  }
+  factory Message.fromJson(Map<String, dynamic> json) {
+    final jsonrpc = json['jsonrpc'];
+    return new Message(jsonrpc);
+  }
 
   final String jsonrpc;
 
@@ -2035,10 +4051,30 @@
         jsonrpc ?? (throw 'jsonrpc is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('jsonrpc') || !map['jsonrpc'] is String) {
+      return false;
+    }
+    const validFieldNames = ['jsonrpc'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class MessageActionItem {
-  MessageActionItem(this.title);
+  MessageActionItem(this.title) {
+    if (title == null) {
+      throw 'title is required but was not provided';
+    }
+  }
+  factory MessageActionItem.fromJson(Map<String, dynamic> json) {
+    final title = json['title'];
+    return new MessageActionItem(title);
+  }
 
   /// A short title like 'Retry', 'Open Log' etc.
   final String title;
@@ -2048,13 +4084,37 @@
     __result['title'] = title ?? (throw 'title is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('title') || !map['title'] is String) {
+      return false;
+    }
+    const validFieldNames = ['title'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class MessageType {
   const MessageType._(this._value);
+  const MessageType.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+        return true;
+    }
+    return false;
+  }
+
   /// An error message.
   static const Error = const MessageType._(1);
 
@@ -2079,7 +4139,19 @@
 }
 
 class NotificationMessage implements Message {
-  NotificationMessage(this.method, this.jsonrpc);
+  NotificationMessage(this.method, this.jsonrpc) {
+    if (method == null) {
+      throw 'method is required but was not provided';
+    }
+    if (jsonrpc == null) {
+      throw 'jsonrpc is required but was not provided';
+    }
+  }
+  factory NotificationMessage.fromJson(Map<String, dynamic> json) {
+    final method = json['method'];
+    final jsonrpc = json['jsonrpc'];
+    return new NotificationMessage(method, jsonrpc);
+  }
 
   final String jsonrpc;
 
@@ -2093,15 +4165,44 @@
         jsonrpc ?? (throw 'jsonrpc is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('method') || !map['method'] is String) {
+      return false;
+    }
+    if (!map.containsKey('jsonrpc') || !map['jsonrpc'] is String) {
+      return false;
+    }
+    const validFieldNames = ['method', 'jsonrpc'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Represents a parameter of a callable-signature. A parameter can have a label
-/// and a doc-comment.
+/// Represents a parameter of a callable-signature. A
+/// parameter can have a label and a doc-comment.
 class ParameterInformation {
-  ParameterInformation(this.label, this.documentation);
+  ParameterInformation(this.label, this.documentation) {
+    if (label == null) {
+      throw 'label is required but was not provided';
+    }
+  }
+  factory ParameterInformation.fromJson(Map<String, dynamic> json) {
+    final label = json['label'];
+    final documentation = json['documentation'] is String
+        ? new Either2<String, MarkupContent>.t1(json['documentation'])
+        : (MarkupContent.canParse(json['documentation'])
+            ? new Either2<String, MarkupContent>.t2(
+                new MarkupContent.fromJson(json['documentation']))
+            : (throw '''${json['documentation']} was not one of (string, MarkupContent)'''));
+    return new ParameterInformation(label, documentation);
+  }
 
-  /// The human-readable doc-comment of this parameter. Will be shown in the UI
-  /// but can be omitted.
+  /// The human-readable doc-comment of this parameter. Will
+  /// be shown in the UI but can be omitted.
   final Either2<String, MarkupContent> documentation;
 
   /// The label of this parameter. Will be shown in the UI.
@@ -2115,17 +4216,42 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('label') || !map['label'] is String) {
+      return false;
+    }
+    const validFieldNames = ['label', 'documentation'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class Position {
-  Position(this.line, this.character);
+  Position(this.line, this.character) {
+    if (line == null) {
+      throw 'line is required but was not provided';
+    }
+    if (character == null) {
+      throw 'character is required but was not provided';
+    }
+  }
+  factory Position.fromJson(Map<String, dynamic> json) {
+    final line = json['line'];
+    final character = json['character'];
+    return new Position(line, character);
+  }
 
-  /// Character offset on a line in a document (zero-based). Assuming that the
-  /// line is represented as a string, the `character` value represents the gap
-  /// between the `character` and `character + 1`.
+  /// Character offset on a line in a document (zero-based).
+  /// Assuming that the line is represented as a string, the
+  /// `character` value represents the gap between the
+  /// `character` and `character + 1`.
   ///
-  /// If the character value is greater than the line length it defaults back to
-  /// the line length.
+  /// If the character value is greater than the line length
+  /// it defaults back to the line length.
   final num character;
 
   /// Line position in a document (zero-based).
@@ -2138,10 +4264,40 @@
         character ?? (throw 'character is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('line') || !map['line'] is num) {
+      return false;
+    }
+    if (!map.containsKey('character') || !map['character'] is num) {
+      return false;
+    }
+    const validFieldNames = ['line', 'character'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class PublishDiagnosticsParams {
-  PublishDiagnosticsParams(this.uri, this.diagnostics);
+  PublishDiagnosticsParams(this.uri, this.diagnostics) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+    if (diagnostics == null) {
+      throw 'diagnostics is required but was not provided';
+    }
+  }
+  factory PublishDiagnosticsParams.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    final diagnostics = json['diagnostics']
+        ?.map((item) => new Diagnostic.fromJson(item))
+        ?.cast<Diagnostic>()
+        ?.toList();
+    return new PublishDiagnosticsParams(uri, diagnostics);
+  }
 
   /// An array of diagnostic information items.
   final List<Diagnostic> diagnostics;
@@ -2156,10 +4312,41 @@
         diagnostics ?? (throw 'diagnostics is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    if (!map.containsKey('diagnostics') ||
+        !(map['diagnostics'] is List &&
+            (map['diagnostics'].length == 0 ||
+                map['diagnostics']
+                    .every((item) => Diagnostic.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['uri', 'diagnostics'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class Range {
-  Range(this.start, this.end);
+  Range(this.start, this.end) {
+    if (start == null) {
+      throw 'start is required but was not provided';
+    }
+    if (end == null) {
+      throw 'end is required but was not provided';
+    }
+  }
+  factory Range.fromJson(Map<String, dynamic> json) {
+    final start = new Position.fromJson(json['start']);
+    final end = new Position.fromJson(json['end']);
+    return new Range(start, end);
+  }
 
   /// The range's end position.
   final Position end;
@@ -2173,10 +4360,33 @@
     __result['end'] = end ?? (throw 'end is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('start') || !Position.canParse(map['start'])) {
+      return false;
+    }
+    if (!map.containsKey('end') || !Position.canParse(map['end'])) {
+      return false;
+    }
+    const validFieldNames = ['start', 'end'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ReferenceContext {
-  ReferenceContext(this.includeDeclaration);
+  ReferenceContext(this.includeDeclaration) {
+    if (includeDeclaration == null) {
+      throw 'includeDeclaration is required but was not provided';
+    }
+  }
+  factory ReferenceContext.fromJson(Map<String, dynamic> json) {
+    final includeDeclaration = json['includeDeclaration'];
+    return new ReferenceContext(includeDeclaration);
+  }
 
   /// Include the declaration of the current symbol.
   final bool includeDeclaration;
@@ -2187,10 +4397,40 @@
         (throw 'includeDeclaration is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('includeDeclaration') ||
+        !map['includeDeclaration'] is bool) {
+      return false;
+    }
+    const validFieldNames = ['includeDeclaration'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ReferenceParams implements TextDocumentPositionParams {
-  ReferenceParams(this.context, this.textDocument, this.position);
+  ReferenceParams(this.context, this.textDocument, this.position) {
+    if (context == null) {
+      throw 'context is required but was not provided';
+    }
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (position == null) {
+      throw 'position is required but was not provided';
+    }
+  }
+  factory ReferenceParams.fromJson(Map<String, dynamic> json) {
+    final context = new ReferenceContext.fromJson(json['context']);
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final position = new Position.fromJson(json['position']);
+    return new ReferenceParams(context, textDocument, position);
+  }
 
   final ReferenceContext context;
 
@@ -2210,14 +4450,47 @@
         position ?? (throw 'position is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('context') ||
+        !ReferenceContext.canParse(map['context'])) {
+      return false;
+    }
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('position') || !Position.canParse(map['position'])) {
+      return false;
+    }
+    const validFieldNames = ['context', 'textDocument', 'position'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// General parameters to register for a capability.
 class Registration {
-  Registration(this.id, this.method, this.registerOptions);
+  Registration(this.id, this.method, this.registerOptions) {
+    if (id == null) {
+      throw 'id is required but was not provided';
+    }
+    if (method == null) {
+      throw 'method is required but was not provided';
+    }
+  }
+  factory Registration.fromJson(Map<String, dynamic> json) {
+    final id = json['id'];
+    final method = json['method'];
+    final registerOptions = json['registerOptions'];
+    return new Registration(id, method, registerOptions);
+  }
 
-  /// The id used to register the request. The id can be used to deregister the
-  /// request again.
+  /// The id used to register the request. The id can be
+  /// used to deregister the request again.
   final String id;
 
   /// The method / capability to register for.
@@ -2235,10 +4508,36 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('id') || !map['id'] is String) {
+      return false;
+    }
+    if (!map.containsKey('method') || !map['method'] is String) {
+      return false;
+    }
+    const validFieldNames = ['id', 'method', 'registerOptions'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class RegistrationParams {
-  RegistrationParams(this.registrations);
+  RegistrationParams(this.registrations) {
+    if (registrations == null) {
+      throw 'registrations is required but was not provided';
+    }
+  }
+  factory RegistrationParams.fromJson(Map<String, dynamic> json) {
+    final registrations = json['registrations']
+        ?.map((item) => new Registration.fromJson(item))
+        ?.cast<Registration>()
+        ?.toList();
+    return new RegistrationParams(registrations);
+  }
 
   final List<Registration> registrations;
 
@@ -2248,11 +4547,40 @@
         registrations ?? (throw 'registrations is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('registrations') ||
+        !(map['registrations'] is List &&
+            (map['registrations'].length == 0 ||
+                map['registrations']
+                    .every((item) => Registration.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['registrations'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Rename file operation
 class RenameFile implements FileOperation {
-  RenameFile(this.oldUri, this.newUri, this.options);
+  RenameFile(this.oldUri, this.newUri, this.options) {
+    if (oldUri == null) {
+      throw 'oldUri is required but was not provided';
+    }
+    if (newUri == null) {
+      throw 'newUri is required but was not provided';
+    }
+  }
+  factory RenameFile.fromJson(Map<String, dynamic> json) {
+    final oldUri = json['oldUri'];
+    final newUri = json['newUri'];
+    final options = new RenameFileOptions.fromJson(json['options']);
+    return new RenameFile(oldUri, newUri, options);
+  }
 
   /// The new location.
   final String newUri;
@@ -2272,16 +4600,37 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('oldUri') || !map['oldUri'] is String) {
+      return false;
+    }
+    if (!map.containsKey('newUri') || !map['newUri'] is String) {
+      return false;
+    }
+    const validFieldNames = ['oldUri', 'newUri', 'options'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Rename file options
 class RenameFileOptions {
   RenameFileOptions(this.overwrite, this.ignoreIfExists);
+  factory RenameFileOptions.fromJson(Map<String, dynamic> json) {
+    final overwrite = json['overwrite'];
+    final ignoreIfExists = json['ignoreIfExists'];
+    return new RenameFileOptions(overwrite, ignoreIfExists);
+  }
 
   /// Ignores if target exists.
   final bool ignoreIfExists;
 
-  /// Overwrite target if existing. Overwrite wins over `ignoreIfExists`
+  /// Overwrite target if existing. Overwrite wins over
+  /// `ignoreIfExists`
   final bool overwrite;
 
   Map<String, dynamic> toJson() {
@@ -2294,13 +4643,27 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['overwrite', 'ignoreIfExists'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Rename options
 class RenameOptions {
   RenameOptions(this.prepareProvider);
+  factory RenameOptions.fromJson(Map<String, dynamic> json) {
+    final prepareProvider = json['prepareProvider'];
+    return new RenameOptions(prepareProvider);
+  }
 
-  /// Renames should be checked and tested before being executed.
+  /// Renames should be checked and tested before being
+  /// executed.
   final bool prepareProvider;
 
   Map<String, dynamic> toJson() {
@@ -2310,13 +4673,40 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['prepareProvider'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class RenameParams {
-  RenameParams(this.textDocument, this.position, this.newName);
+  RenameParams(this.textDocument, this.position, this.newName) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (position == null) {
+      throw 'position is required but was not provided';
+    }
+    if (newName == null) {
+      throw 'newName is required but was not provided';
+    }
+  }
+  factory RenameParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final position = new Position.fromJson(json['position']);
+    final newName = json['newName'];
+    return new RenameParams(textDocument, position, newName);
+  }
 
-  /// The new name of the symbol. If the given name is not valid the request
-  /// must return a [ResponseError] with an appropriate message set.
+  /// The new name of the symbol. If the given name is not
+  /// valid the request must return a [ResponseError] with
+  /// an appropriate message set.
   final String newName;
 
   /// The position at which this request was sent.
@@ -2335,16 +4725,45 @@
         newName ?? (throw 'newName is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('position') || !Position.canParse(map['position'])) {
+      return false;
+    }
+    if (!map.containsKey('newName') || !map['newName'] is String) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'position', 'newName'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class RenameRegistrationOptions implements TextDocumentRegistrationOptions {
   RenameRegistrationOptions(this.prepareProvider, this.documentSelector);
+  factory RenameRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final prepareProvider = json['prepareProvider'];
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new RenameRegistrationOptions(prepareProvider, documentSelector);
+  }
 
-  /// A document selector to identify the scope of the registration. If set to
-  /// null the document selector provided on the client side will be used.
+  /// A document selector to identify the scope of the
+  /// registration. If set to null the document selector
+  /// provided on the client side will be used.
   final List<DocumentFilter> documentSelector;
 
-  /// Renames should be checked and tested for validity before being executed.
+  /// Renames should be checked and tested for validity
+  /// before being executed.
   final bool prepareProvider;
 
   Map<String, dynamic> toJson() {
@@ -2355,10 +4774,46 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['prepareProvider', 'documentSelector'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class RequestMessage implements Message {
-  RequestMessage(this.id, this.method, this.jsonrpc);
+  RequestMessage(this.id, this.method, this.jsonrpc) {
+    if (id == null) {
+      throw 'id is required but was not provided';
+    }
+    if (method == null) {
+      throw 'method is required but was not provided';
+    }
+    if (jsonrpc == null) {
+      throw 'jsonrpc is required but was not provided';
+    }
+  }
+  factory RequestMessage.fromJson(Map<String, dynamic> json) {
+    final id = json['id'] is num
+        ? new Either2<num, String>.t1(json['id'])
+        : (json['id'] is String
+            ? new Either2<num, String>.t2(json['id'])
+            : (throw '''${json['id']} was not one of (number, string)'''));
+    final method = json['method'];
+    final jsonrpc = json['jsonrpc'];
+    return new RequestMessage(id, method, jsonrpc);
+  }
 
   /// The request id.
   final Either2<num, String> id;
@@ -2375,13 +4830,42 @@
         jsonrpc ?? (throw 'jsonrpc is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('id') || !(map['id'] is num || map['id'] is String)) {
+      return false;
+    }
+    if (!map.containsKey('method') || !map['method'] is String) {
+      return false;
+    }
+    if (!map.containsKey('jsonrpc') || !map['jsonrpc'] is String) {
+      return false;
+    }
+    const validFieldNames = ['id', 'method', 'jsonrpc'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ResourceOperationKind {
   const ResourceOperationKind._(this._value);
+  const ResourceOperationKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 'create':
+      case 'rename':
+      case 'delete':
+        return true;
+    }
+    return false;
+  }
+
   /// Supports creating new files and folders.
   static const Create = const ResourceOperationKind._('create');
 
@@ -2403,13 +4887,28 @@
 }
 
 class ResponseMessage implements Message {
-  ResponseMessage(this.id, this.result, this.jsonrpc);
+  ResponseMessage(this.id, this.result, this.jsonrpc) {
+    if (jsonrpc == null) {
+      throw 'jsonrpc is required but was not provided';
+    }
+  }
+  factory ResponseMessage.fromJson(Map<String, dynamic> json) {
+    final id = json['id'] is num
+        ? new Either2<num, String>.t1(json['id'])
+        : (json['id'] is String
+            ? new Either2<num, String>.t2(json['id'])
+            : (throw '''${json['id']} was not one of (number, string)'''));
+    final result = json['result'];
+    final jsonrpc = json['jsonrpc'];
+    return new ResponseMessage(id, result, jsonrpc);
+  }
 
   /// The request id.
   final Either2<num, String> id;
   final String jsonrpc;
 
-  /// The result of a request. This can be omitted in the case of an error.
+  /// The result of a request. This can be omitted in the
+  /// case of an error.
   final dynamic result;
 
   Map<String, dynamic> toJson() {
@@ -2422,13 +4921,33 @@
         jsonrpc ?? (throw 'jsonrpc is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('id') || !(map['id'] is num || map['id'] is String)) {
+      return false;
+    }
+    if (!map.containsKey('jsonrpc') || !map['jsonrpc'] is String) {
+      return false;
+    }
+    const validFieldNames = ['id', 'result', 'jsonrpc'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Save options.
 class SaveOptions {
   SaveOptions(this.includeText);
+  factory SaveOptions.fromJson(Map<String, dynamic> json) {
+    final includeText = json['includeText'];
+    return new SaveOptions(includeText);
+  }
 
-  /// The client is supposed to include the content on save.
+  /// The client is supposed to include the content on
+  /// save.
   final bool includeText;
 
   Map<String, dynamic> toJson() {
@@ -2438,6 +4957,15 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['includeText'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ServerCapabilities {
@@ -2461,18 +4989,92 @@
       this.executeCommandProvider,
       this.supported,
       this.changeNotifications);
+  factory ServerCapabilities.fromJson(Map<String, dynamic> json) {
+    final textDocumentSync = TextDocumentSyncOptions.canParse(
+            json['textDocumentSync'])
+        ? new Either2<TextDocumentSyncOptions, num>.t1(
+            new TextDocumentSyncOptions.fromJson(json['textDocumentSync']))
+        : (json['textDocumentSync'] is num
+            ? new Either2<TextDocumentSyncOptions, num>.t2(
+                json['textDocumentSync'])
+            : (throw '''${json['textDocumentSync']} was not one of (TextDocumentSyncOptions, number)'''));
+    final hoverProvider = json['hoverProvider'];
+    final completionProvider =
+        new CompletionOptions.fromJson(json['completionProvider']);
+    final signatureHelpProvider =
+        new SignatureHelpOptions.fromJson(json['signatureHelpProvider']);
+    final definitionProvider = json['definitionProvider'];
+    final referencesProvider = json['referencesProvider'];
+    final documentHighlightProvider = json['documentHighlightProvider'];
+    final documentSymbolProvider = json['documentSymbolProvider'];
+    final workspaceSymbolProvider = json['workspaceSymbolProvider'];
+    final codeActionProvider = json['codeActionProvider'] is bool
+        ? new Either2<bool, CodeActionOptions>.t1(json['codeActionProvider'])
+        : (CodeActionOptions.canParse(json['codeActionProvider'])
+            ? new Either2<bool, CodeActionOptions>.t2(
+                new CodeActionOptions.fromJson(json['codeActionProvider']))
+            : (throw '''${json['codeActionProvider']} was not one of (boolean, CodeActionOptions)'''));
+    final codeLensProvider =
+        new CodeLensOptions.fromJson(json['codeLensProvider']);
+    final documentFormattingProvider = json['documentFormattingProvider'];
+    final documentRangeFormattingProvider =
+        json['documentRangeFormattingProvider'];
+    final documentOnTypeFormattingProvider =
+        new DocumentOnTypeFormattingOptions.fromJson(
+            json['documentOnTypeFormattingProvider']);
+    final renameProvider = json['renameProvider'] is bool
+        ? new Either2<bool, RenameOptions>.t1(json['renameProvider'])
+        : (RenameOptions.canParse(json['renameProvider'])
+            ? new Either2<bool, RenameOptions>.t2(
+                new RenameOptions.fromJson(json['renameProvider']))
+            : (throw '''${json['renameProvider']} was not one of (boolean, RenameOptions)'''));
+    final documentLinkProvider =
+        new DocumentLinkOptions.fromJson(json['documentLinkProvider']);
+    final executeCommandProvider =
+        new ExecuteCommandOptions.fromJson(json['executeCommandProvider']);
+    final supported = json['supported'];
+    final changeNotifications = json['changeNotifications'] is String
+        ? new Either2<String, bool>.t1(json['changeNotifications'])
+        : (json['changeNotifications'] is bool
+            ? new Either2<String, bool>.t2(json['changeNotifications'])
+            : (throw '''${json['changeNotifications']} was not one of (string, boolean)'''));
+    return new ServerCapabilities(
+        textDocumentSync,
+        hoverProvider,
+        completionProvider,
+        signatureHelpProvider,
+        definitionProvider,
+        referencesProvider,
+        documentHighlightProvider,
+        documentSymbolProvider,
+        workspaceSymbolProvider,
+        codeActionProvider,
+        codeLensProvider,
+        documentFormattingProvider,
+        documentRangeFormattingProvider,
+        documentOnTypeFormattingProvider,
+        renameProvider,
+        documentLinkProvider,
+        executeCommandProvider,
+        supported,
+        changeNotifications);
+  }
 
-  /// Whether the server wants to receive workspace folder change notifications.
+  /// Whether the server wants to receive workspace folder
+  /// change notifications.
   ///
-  /// If a strings is provided the string is treated as a ID under which the
-  /// notification is registered on the client side. The ID can be used to
-  /// unregister for these events using the `client/unregisterCapability`
+  /// If a strings is provided the string is treated as a
+  /// ID under which the notification is registered on the
+  /// client side. The ID can be used to unregister for
+  /// these events using the `client/unregisterCapability`
   /// request.
   final Either2<String, bool> changeNotifications;
 
-  /// The server provides code actions. The `CodeActionOptions` return type is
-  /// only valid if the client signals code action literal support via the
-  /// property `textDocument.codeAction.codeActionLiteralSupport`.
+  /// The server provides code actions. The
+  /// `CodeActionOptions` return type is only valid if the
+  /// client signals code action literal support via the
+  /// property
+  /// `textDocument.codeAction.codeActionLiteralSupport`.
   final Either2<bool, CodeActionOptions> codeActionProvider;
 
   /// The server provides code lens.
@@ -2511,8 +5113,9 @@
   /// The server provides find references support.
   final bool referencesProvider;
 
-  /// The server provides rename support. RenameOptions may only be specified if
-  /// the client states that it supports `prepareSupport` in its initial
+  /// The server provides rename support. RenameOptions
+  /// may only be specified if the client states that it
+  /// supports `prepareSupport` in its initial
   /// `initialize` request.
   final Either2<bool, RenameOptions> renameProvider;
 
@@ -2522,9 +5125,10 @@
   /// The server has support for workspace folders
   final bool supported;
 
-  /// Defines how text documents are synced. Is either a detailed structure
-  /// defining each notification or for backwards compatibility the
-  /// TextDocumentSyncKind number. If omitted it defaults to
+  /// Defines how text documents are synced. Is either a
+  /// detailed structure defining each notification or for
+  /// backwards compatibility the TextDocumentSyncKind
+  /// number. If omitted it defaults to
   /// `TextDocumentSyncKind.None`.
   final Either2<TextDocumentSyncOptions, num> textDocumentSync;
 
@@ -2594,10 +5198,51 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = [
+      'textDocumentSync',
+      'hoverProvider',
+      'completionProvider',
+      'signatureHelpProvider',
+      'definitionProvider',
+      'referencesProvider',
+      'documentHighlightProvider',
+      'documentSymbolProvider',
+      'workspaceSymbolProvider',
+      'codeActionProvider',
+      'codeLensProvider',
+      'documentFormattingProvider',
+      'documentRangeFormattingProvider',
+      'documentOnTypeFormattingProvider',
+      'renameProvider',
+      'documentLinkProvider',
+      'executeCommandProvider',
+      'supported',
+      'changeNotifications'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ShowMessageParams {
-  ShowMessageParams(this.type, this.message);
+  ShowMessageParams(this.type, this.message) {
+    if (type == null) {
+      throw 'type is required but was not provided';
+    }
+    if (message == null) {
+      throw 'message is required but was not provided';
+    }
+  }
+  factory ShowMessageParams.fromJson(Map<String, dynamic> json) {
+    final type = new MessageType.fromJson(json['type']);
+    final message = json['message'];
+    return new ShowMessageParams(type, message);
+  }
 
   /// The actual message.
   final String message;
@@ -2612,10 +5257,41 @@
         message ?? (throw 'message is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('type') || !MessageType.canParse(map['type'])) {
+      return false;
+    }
+    if (!map.containsKey('message') || !map['message'] is String) {
+      return false;
+    }
+    const validFieldNames = ['type', 'message'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class ShowMessageRequestParams {
-  ShowMessageRequestParams(this.type, this.message, this.actions);
+  ShowMessageRequestParams(this.type, this.message, this.actions) {
+    if (type == null) {
+      throw 'type is required but was not provided';
+    }
+    if (message == null) {
+      throw 'message is required but was not provided';
+    }
+  }
+  factory ShowMessageRequestParams.fromJson(Map<String, dynamic> json) {
+    final type = new MessageType.fromJson(json['type']);
+    final message = json['message'];
+    final actions = json['actions']
+        ?.map((item) => new MessageActionItem.fromJson(item))
+        ?.cast<MessageActionItem>()
+        ?.toList();
+    return new ShowMessageRequestParams(type, message, actions);
+  }
 
   /// The message action items to present.
   final List<MessageActionItem> actions;
@@ -2636,27 +5312,60 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('type') || !MessageType.canParse(map['type'])) {
+      return false;
+    }
+    if (!map.containsKey('message') || !map['message'] is String) {
+      return false;
+    }
+    const validFieldNames = ['type', 'message', 'actions'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Signature help represents the signature of something callable. There can be
-/// multiple signature but only one active and only one active parameter.
+/// Signature help represents the signature of something
+/// callable. There can be multiple signature but only one
+/// active and only one active parameter.
 class SignatureHelp {
-  SignatureHelp(this.signatures, this.activeSignature, this.activeParameter);
+  SignatureHelp(this.signatures, this.activeSignature, this.activeParameter) {
+    if (signatures == null) {
+      throw 'signatures is required but was not provided';
+    }
+  }
+  factory SignatureHelp.fromJson(Map<String, dynamic> json) {
+    final signatures = json['signatures']
+        ?.map((item) => new SignatureInformation.fromJson(item))
+        ?.cast<SignatureInformation>()
+        ?.toList();
+    final activeSignature = json['activeSignature'];
+    final activeParameter = json['activeParameter'];
+    return new SignatureHelp(signatures, activeSignature, activeParameter);
+  }
 
-  /// The active parameter of the active signature. If omitted or the value lies
-  /// outside the range of `signatures[activeSignature].parameters` defaults to
-  /// 0 if the active signature has parameters. If the active signature has no
-  /// parameters it is ignored. In future version of the protocol this property
-  /// might become mandatory to better express the active parameter if the
-  /// active signature does have any.
+  /// The active parameter of the active signature. If
+  /// omitted or the value lies outside the range of
+  /// `signatures[activeSignature].parameters` defaults to
+  /// 0 if the active signature has parameters. If the
+  /// active signature has no parameters it is ignored. In
+  /// future version of the protocol this property might
+  /// become mandatory to better express the active
+  /// parameter if the active signature does have any.
   final num activeParameter;
 
-  /// The active signature. If omitted or the value lies outside the range of
-  /// `signatures` the value defaults to zero or is ignored if
-  /// `signatures.length === 0`. Whenever possible implementors should make an
-  /// active decision about the active signature and shouldn't rely on a default
-  /// value. In future version of the protocol this property might become
-  /// mandatory to better express this.
+  /// The active signature. If omitted or the value lies
+  /// outside the range of `signatures` the value defaults
+  /// to zero or is ignored if `signatures.length === 0`.
+  /// Whenever possible implementors should make an active
+  /// decision about the active signature and shouldn't
+  /// rely on a default value. In future version of the
+  /// protocol this property might become mandatory to
+  /// better express this.
   final num activeSignature;
 
   /// One or more signatures.
@@ -2674,13 +5383,41 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('signatures') ||
+        !(map['signatures'] is List &&
+            (map['signatures'].length == 0 ||
+                map['signatures']
+                    .every((item) => SignatureInformation.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = [
+      'signatures',
+      'activeSignature',
+      'activeParameter'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Signature help options.
 class SignatureHelpOptions {
   SignatureHelpOptions(this.triggerCharacters);
+  factory SignatureHelpOptions.fromJson(Map<String, dynamic> json) {
+    final triggerCharacters = json['triggerCharacters']
+        ?.map((item) => item)
+        ?.cast<String>()
+        ?.toList();
+    return new SignatureHelpOptions(triggerCharacters);
+  }
 
-  /// The characters that trigger signature help automatically.
+  /// The characters that trigger signature help
+  /// automatically.
   final List<String> triggerCharacters;
 
   Map<String, dynamic> toJson() {
@@ -2690,18 +5427,41 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['triggerCharacters'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class SignatureHelpRegistrationOptions
     implements TextDocumentRegistrationOptions {
   SignatureHelpRegistrationOptions(
       this.triggerCharacters, this.documentSelector);
+  factory SignatureHelpRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final triggerCharacters = json['triggerCharacters']
+        ?.map((item) => item)
+        ?.cast<String>()
+        ?.toList();
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new SignatureHelpRegistrationOptions(
+        triggerCharacters, documentSelector);
+  }
 
-  /// A document selector to identify the scope of the registration. If set to
-  /// null the document selector provided on the client side will be used.
+  /// A document selector to identify the scope of the
+  /// registration. If set to null the document selector
+  /// provided on the client side will be used.
   final List<DocumentFilter> documentSelector;
 
-  /// The characters that trigger signature help automatically.
+  /// The characters that trigger signature help
+  /// automatically.
   final List<String> triggerCharacters;
 
   Map<String, dynamic> toJson() {
@@ -2712,18 +5472,54 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['triggerCharacters', 'documentSelector'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Represents the signature of something callable. A signature can have a
-/// label, like a function-name, a doc-comment, and a set of parameters.
+/// Represents the signature of something callable. A
+/// signature can have a label, like a function-name, a
+/// doc-comment, and a set of parameters.
 class SignatureInformation {
-  SignatureInformation(this.label, this.documentation, this.parameters);
+  SignatureInformation(this.label, this.documentation, this.parameters) {
+    if (label == null) {
+      throw 'label is required but was not provided';
+    }
+  }
+  factory SignatureInformation.fromJson(Map<String, dynamic> json) {
+    final label = json['label'];
+    final documentation = json['documentation'] is String
+        ? new Either2<String, MarkupContent>.t1(json['documentation'])
+        : (MarkupContent.canParse(json['documentation'])
+            ? new Either2<String, MarkupContent>.t2(
+                new MarkupContent.fromJson(json['documentation']))
+            : (throw '''${json['documentation']} was not one of (string, MarkupContent)'''));
+    final parameters = json['parameters']
+        ?.map((item) => new ParameterInformation.fromJson(item))
+        ?.cast<ParameterInformation>()
+        ?.toList();
+    return new SignatureInformation(label, documentation, parameters);
+  }
 
-  /// The human-readable doc-comment of this signature. Will be shown in the UI
-  /// but can be omitted.
+  /// The human-readable doc-comment of this signature.
+  /// Will be shown in the UI but can be omitted.
   final Either2<String, MarkupContent> documentation;
 
-  /// The label of this signature. Will be shown in the UI.
+  /// The label of this signature. Will be shown in the
+  /// UI.
   final String label;
 
   /// The parameters of this signature.
@@ -2740,14 +5536,32 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('label') || !map['label'] is String) {
+      return false;
+    }
+    const validFieldNames = ['label', 'documentation', 'parameters'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Static registration options to be returned in the initialize request.
+/// Static registration options to be returned in the
+/// initialize request.
 class StaticRegistrationOptions {
   StaticRegistrationOptions(this.id);
+  factory StaticRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final id = json['id'];
+    return new StaticRegistrationOptions(id);
+  }
 
-  /// The id used to register the request. The id can be used to deregister the
-  /// request again. See also Registration#id.
+  /// The id used to register the request. The id can be
+  /// used to deregister the request again. See also
+  /// Registration#id.
   final String id;
 
   Map<String, dynamic> toJson() {
@@ -2757,18 +5571,47 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['id'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Represents information about programming constructs like variables, classes,
-/// interfaces etc.
+/// Represents information about programming constructs
+/// like variables, classes, interfaces etc.
 class SymbolInformation {
-  SymbolInformation(
-      this.name, this.kind, this.deprecated, this.location, this.containerName);
+  SymbolInformation(this.name, this.kind, this.deprecated, this.location,
+      this.containerName) {
+    if (name == null) {
+      throw 'name is required but was not provided';
+    }
+    if (kind == null) {
+      throw 'kind is required but was not provided';
+    }
+    if (location == null) {
+      throw 'location is required but was not provided';
+    }
+  }
+  factory SymbolInformation.fromJson(Map<String, dynamic> json) {
+    final name = json['name'];
+    final kind = json['kind'];
+    final deprecated = json['deprecated'];
+    final location = new Location.fromJson(json['location']);
+    final containerName = json['containerName'];
+    return new SymbolInformation(
+        name, kind, deprecated, location, containerName);
+  }
 
-  /// The name of the symbol containing this symbol. This information is for
-  /// user interface purposes (e.g. to render a qualifier in the user interface
-  /// if necessary). It can't be used to re-infer a hierarchy for the document
-  /// symbols.
+  /// The name of the symbol containing this symbol. This
+  /// information is for user interface purposes (e.g. to
+  /// render a qualifier in the user interface if
+  /// necessary). It can't be used to re-infer a hierarchy
+  /// for the document symbols.
   final String containerName;
 
   /// Indicates if this symbol is deprecated.
@@ -2777,15 +5620,18 @@
   /// The kind of this symbol.
   final num kind;
 
-  /// The location of this symbol. The location's range is used by a tool to
-  /// reveal the location in the editor. If the symbol is selected in the tool
-  /// the range's start information is used to position the cursor. So the range
-  /// usually spans more then the actual symbol's name and does normally include
-  /// things like visibility modifiers.
+  /// The location of this symbol. The location's range is
+  /// used by a tool to reveal the location in the editor.
+  /// If the symbol is selected in the tool the range's
+  /// start information is used to position the cursor. So
+  /// the range usually spans more then the actual
+  /// symbol's name and does normally include things like
+  /// visibility modifiers.
   ///
-  /// The range doesn't have to denote a node range in the sense of a abstract
-  /// syntax tree. It can therefore not be used to re-construct a hierarchy of
-  /// the symbols.
+  /// The range doesn't have to denote a node range in the
+  /// sense of a abstract syntax tree. It can therefore
+  /// not be used to re-construct a hierarchy of the
+  /// symbols.
   final Location location;
 
   /// The name of this symbol.
@@ -2805,14 +5651,72 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('name') || !map['name'] is String) {
+      return false;
+    }
+    if (!map.containsKey('kind') || !map['kind'] is num) {
+      return false;
+    }
+    if (!map.containsKey('location') || !Location.canParse(map['location'])) {
+      return false;
+    }
+    const validFieldNames = [
+      'name',
+      'kind',
+      'deprecated',
+      'location',
+      'containerName'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// A symbol kind.
 class SymbolKind {
   const SymbolKind._(this._value);
+  const SymbolKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+      case 4:
+      case 5:
+      case 6:
+      case 7:
+      case 8:
+      case 9:
+      case 10:
+      case 11:
+      case 12:
+      case 13:
+      case 14:
+      case 15:
+      case 16:
+      case 17:
+      case 18:
+      case 19:
+      case 20:
+      case 21:
+      case 22:
+      case 23:
+      case 24:
+      case 25:
+      case 26:
+        return true;
+    }
+    return false;
+  }
+
   static const File = const SymbolKind._(1);
   static const Module = const SymbolKind._(2);
   static const Namespace = const SymbolKind._(3);
@@ -2851,17 +5755,33 @@
   bool operator ==(o) => o is SymbolKind && o._value == _value;
 }
 
-/// Describe options to be used when registering for text document change
-/// events.
+/// Describe options to be used when registering for
+/// text document change events.
 class TextDocumentChangeRegistrationOptions
     implements TextDocumentRegistrationOptions {
-  TextDocumentChangeRegistrationOptions(this.syncKind, this.documentSelector);
+  TextDocumentChangeRegistrationOptions(this.syncKind, this.documentSelector) {
+    if (syncKind == null) {
+      throw 'syncKind is required but was not provided';
+    }
+  }
+  factory TextDocumentChangeRegistrationOptions.fromJson(
+      Map<String, dynamic> json) {
+    final syncKind = json['syncKind'];
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new TextDocumentChangeRegistrationOptions(
+        syncKind, documentSelector);
+  }
 
-  /// A document selector to identify the scope of the registration. If set to
-  /// null the document selector provided on the client side will be used.
+  /// A document selector to identify the scope of the
+  /// registration. If set to null the document selector
+  /// provided on the client side will be used.
   final List<DocumentFilter> documentSelector;
 
-  /// How documents are synced to the server. See TextDocumentSyncKind.Full and
+  /// How documents are synced to the server. See
+  /// TextDocumentSyncKind.Full and
   /// TextDocumentSyncKind.Incremental.
   final num syncKind;
 
@@ -2872,24 +5792,54 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('syncKind') || !map['syncKind'] is num) {
+      return false;
+    }
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['syncKind', 'documentSelector'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Text document specific client capabilities.
 class TextDocumentClientCapabilities {
   TextDocumentClientCapabilities(this.dynamicRegistration, this.willSave,
       this.willSaveWaitUntil, this.didSave);
+  factory TextDocumentClientCapabilities.fromJson(Map<String, dynamic> json) {
+    final dynamicRegistration = json['dynamicRegistration'];
+    final willSave = json['willSave'];
+    final willSaveWaitUntil = json['willSaveWaitUntil'];
+    final didSave = json['didSave'];
+    return new TextDocumentClientCapabilities(
+        dynamicRegistration, willSave, willSaveWaitUntil, didSave);
+  }
 
   /// The client supports did save notifications.
   final bool didSave;
 
-  /// Whether text document synchronization supports dynamic registration.
+  /// Whether text document synchronization supports
+  /// dynamic registration.
   final bool dynamicRegistration;
 
-  /// The client supports sending will save notifications.
+  /// The client supports sending will save
+  /// notifications.
   final bool willSave;
 
-  /// The client supports sending a will save request and waits for a response
-  /// providing text edits which will be applied to the document before it is
+  /// The client supports sending a will save request
+  /// and waits for a response providing text edits
+  /// which will be applied to the document before it is
   /// saved.
   final bool willSaveWaitUntil;
 
@@ -2909,13 +5859,37 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = [
+      'dynamicRegistration',
+      'willSave',
+      'willSaveWaitUntil',
+      'didSave'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// An event describing a change to a text document. If range and rangeLength
-/// are omitted the new text is considered to be the full content of the
-/// document.
+/// An event describing a change to a text document. If
+/// range and rangeLength are omitted the new text is
+/// considered to be the full content of the document.
 class TextDocumentContentChangeEvent {
-  TextDocumentContentChangeEvent(this.range, this.rangeLength, this.text);
+  TextDocumentContentChangeEvent(this.range, this.rangeLength, this.text) {
+    if (text == null) {
+      throw 'text is required but was not provided';
+    }
+  }
+  factory TextDocumentContentChangeEvent.fromJson(Map<String, dynamic> json) {
+    final range = new Range.fromJson(json['range']);
+    final rangeLength = json['rangeLength'];
+    final text = json['text'];
+    return new TextDocumentContentChangeEvent(range, rangeLength, text);
+  }
 
   /// The range of the document that changed.
   final Range range;
@@ -2937,10 +5911,38 @@
     __result['text'] = text ?? (throw 'text is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('text') || !map['text'] is String) {
+      return false;
+    }
+    const validFieldNames = ['range', 'rangeLength', 'text'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class TextDocumentEdit implements FileOperation {
-  TextDocumentEdit(this.textDocument, this.edits);
+  TextDocumentEdit(this.textDocument, this.edits) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (edits == null) {
+      throw 'edits is required but was not provided';
+    }
+  }
+  factory TextDocumentEdit.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new VersionedTextDocumentIdentifier.fromJson(json['textDocument']);
+    final edits = json['edits']
+        ?.map((item) => new TextEdit.fromJson(item))
+        ?.cast<TextEdit>()
+        ?.toList();
+    return new TextDocumentEdit(textDocument, edits);
+  }
 
   /// The edits to be applied.
   final List<TextEdit> edits;
@@ -2955,10 +5957,37 @@
     __result['edits'] = edits ?? (throw 'edits is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !VersionedTextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('edits') ||
+        !(map['edits'] is List &&
+            (map['edits'].length == 0 ||
+                map['edits'].every((item) => TextEdit.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'edits'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class TextDocumentIdentifier {
-  TextDocumentIdentifier(this.uri);
+  TextDocumentIdentifier(this.uri) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+  }
+  factory TextDocumentIdentifier.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    return new TextDocumentIdentifier(uri);
+  }
 
   /// The text document's URI.
   final String uri;
@@ -2968,10 +5997,42 @@
     __result['uri'] = uri ?? (throw 'uri is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    const validFieldNames = ['uri'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class TextDocumentItem {
-  TextDocumentItem(this.uri, this.languageId, this.version, this.text);
+  TextDocumentItem(this.uri, this.languageId, this.version, this.text) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+    if (languageId == null) {
+      throw 'languageId is required but was not provided';
+    }
+    if (version == null) {
+      throw 'version is required but was not provided';
+    }
+    if (text == null) {
+      throw 'text is required but was not provided';
+    }
+  }
+  factory TextDocumentItem.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    final languageId = json['languageId'];
+    final version = json['version'];
+    final text = json['text'];
+    return new TextDocumentItem(uri, languageId, version, text);
+  }
 
   /// The text document's language identifier.
   final String languageId;
@@ -2982,8 +6043,8 @@
   /// The text document's URI.
   final String uri;
 
-  /// The version number of this document (it will increase after each change,
-  /// including undo/redo).
+  /// The version number of this document (it will
+  /// increase after each change, including undo/redo).
   final num version;
 
   Map<String, dynamic> toJson() {
@@ -2996,10 +6057,44 @@
     __result['text'] = text ?? (throw 'text is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    if (!map.containsKey('languageId') || !map['languageId'] is String) {
+      return false;
+    }
+    if (!map.containsKey('version') || !map['version'] is num) {
+      return false;
+    }
+    if (!map.containsKey('text') || !map['text'] is String) {
+      return false;
+    }
+    const validFieldNames = ['uri', 'languageId', 'version', 'text'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class TextDocumentPositionParams {
-  TextDocumentPositionParams(this.textDocument, this.position);
+  TextDocumentPositionParams(this.textDocument, this.position) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (position == null) {
+      throw 'position is required but was not provided';
+    }
+  }
+  factory TextDocumentPositionParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final position = new Position.fromJson(json['position']);
+    return new TextDocumentPositionParams(textDocument, position);
+  }
 
   /// The position inside the text document.
   final Position position;
@@ -3015,13 +6110,37 @@
         position ?? (throw 'position is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('position') || !Position.canParse(map['position'])) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'position'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class TextDocumentRegistrationOptions {
   TextDocumentRegistrationOptions(this.documentSelector);
+  factory TextDocumentRegistrationOptions.fromJson(Map<String, dynamic> json) {
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new TextDocumentRegistrationOptions(documentSelector);
+  }
 
-  /// A document selector to identify the scope of the registration. If set to
-  /// null the document selector provided on the client side will be used.
+  /// A document selector to identify the scope of the
+  /// registration. If set to null the document selector
+  /// provided on the client side will be used.
   final List<DocumentFilter> documentSelector;
 
   Map<String, dynamic> toJson() {
@@ -3029,16 +6148,43 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['documentSelector'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Represents reasons why a text document is saved.
 class TextDocumentSaveReason {
   const TextDocumentSaveReason._(this._value);
+  const TextDocumentSaveReason.fromJson(this._value);
 
   final Object _value;
 
-  /// Manually triggered, e.g. by the user pressing save, by starting debugging,
-  /// or by an API call.
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 3:
+        return true;
+    }
+    return false;
+  }
+
+  /// Manually triggered, e.g. by the user pressing
+  /// save, by starting debugging, or by an API call.
   static const Manual = const TextDocumentSaveReason._(1);
 
   /// Automatic after a delay.
@@ -3061,12 +6207,25 @@
 class TextDocumentSaveRegistrationOptions
     implements TextDocumentRegistrationOptions {
   TextDocumentSaveRegistrationOptions(this.includeText, this.documentSelector);
+  factory TextDocumentSaveRegistrationOptions.fromJson(
+      Map<String, dynamic> json) {
+    final includeText = json['includeText'];
+    final documentSelector = json['documentSelector']
+        ?.map((item) => new DocumentFilter.fromJson(item))
+        ?.cast<DocumentFilter>()
+        ?.toList();
+    return new TextDocumentSaveRegistrationOptions(
+        includeText, documentSelector);
+  }
 
-  /// A document selector to identify the scope of the registration. If set to
-  /// null the document selector provided on the client side will be used.
+  /// A document selector to identify the scope of the
+  /// registration. If set to null the document
+  /// selector provided on the client side will be
+  /// used.
   final List<DocumentFilter> documentSelector;
 
-  /// The client is supposed to include the content on save.
+  /// The client is supposed to include the content on
+  /// save.
   final bool includeText;
 
   Map<String, dynamic> toJson() {
@@ -3077,23 +6236,52 @@
     __result['documentSelector'] = documentSelector;
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('documentSelector') ||
+        !(map['documentSelector'] is List &&
+            (map['documentSelector'].length == 0 ||
+                map['documentSelector']
+                    .every((item) => DocumentFilter.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['includeText', 'documentSelector'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
-/// Defines how the host (editor) should sync document changes to the language
-/// server.
+/// Defines how the host (editor) should sync document
+/// changes to the language server.
 class TextDocumentSyncKind {
   const TextDocumentSyncKind._(this._value);
+  const TextDocumentSyncKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 0:
+      case 1:
+      case 2:
+        return true;
+    }
+    return false;
+  }
+
   /// Documents should not be synced at all.
   static const None = const TextDocumentSyncKind._(0);
 
-  /// Documents are synced by always sending the full content of the document.
+  /// Documents are synced by always sending the
+  /// full content of the document.
   static const Full = const TextDocumentSyncKind._(1);
 
-  /// Documents are synced by sending the full content on open. After that only
-  /// incremental updates to the document are send.
+  /// Documents are synced by sending the full
+  /// content on open. After that only incremental
+  /// updates to the document are send.
   static const Incremental = const TextDocumentSyncKind._(2);
 
   Object toJson() => _value;
@@ -3110,23 +6298,36 @@
 class TextDocumentSyncOptions {
   TextDocumentSyncOptions(this.openClose, this.change, this.willSave,
       this.willSaveWaitUntil, this.save);
+  factory TextDocumentSyncOptions.fromJson(Map<String, dynamic> json) {
+    final openClose = json['openClose'];
+    final change = new TextDocumentSyncKind.fromJson(json['change']);
+    final willSave = json['willSave'];
+    final willSaveWaitUntil = json['willSaveWaitUntil'];
+    final save = new SaveOptions.fromJson(json['save']);
+    return new TextDocumentSyncOptions(
+        openClose, change, willSave, willSaveWaitUntil, save);
+  }
 
-  /// Change notifications are sent to the server. See
-  /// TextDocumentSyncKind.None, TextDocumentSyncKind.Full and
-  /// TextDocumentSyncKind.Incremental. If omitted it defaults to
-  /// TextDocumentSyncKind.None.
+  /// Change notifications are sent to the server.
+  /// See TextDocumentSyncKind.None,
+  /// TextDocumentSyncKind.Full and
+  /// TextDocumentSyncKind.Incremental. If omitted
+  /// it defaults to TextDocumentSyncKind.None.
   final TextDocumentSyncKind change;
 
-  /// Open and close notifications are sent to the server.
+  /// Open and close notifications are sent to the
+  /// server.
   final bool openClose;
 
   /// Save notifications are sent to the server.
   final SaveOptions save;
 
-  /// Will save notifications are sent to the server.
+  /// Will save notifications are sent to the
+  /// server.
   final bool willSave;
 
-  /// Will save wait until requests are sent to the server.
+  /// Will save wait until requests are sent to the
+  /// server.
   final bool willSaveWaitUntil;
 
   Map<String, dynamic> toJson() {
@@ -3148,16 +6349,45 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = [
+      'openClose',
+      'change',
+      'willSave',
+      'willSaveWaitUntil',
+      'save'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class TextEdit {
-  TextEdit(this.range, this.newText);
+  TextEdit(this.range, this.newText) {
+    if (range == null) {
+      throw 'range is required but was not provided';
+    }
+    if (newText == null) {
+      throw 'newText is required but was not provided';
+    }
+  }
+  factory TextEdit.fromJson(Map<String, dynamic> json) {
+    final range = new Range.fromJson(json['range']);
+    final newText = json['newText'];
+    return new TextEdit(range, newText);
+  }
 
-  /// The string to be inserted. For delete operations use an empty string.
+  /// The string to be inserted. For delete
+  /// operations use an empty string.
   final String newText;
 
-  /// The range of the text document to be manipulated. To insert text into a
-  /// document create a range where start === end.
+  /// The range of the text document to be
+  /// manipulated. To insert text into a document
+  /// create a range where start === end.
   final Range range;
 
   Map<String, dynamic> toJson() {
@@ -3167,14 +6397,42 @@
         newText ?? (throw 'newText is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('range') || !Range.canParse(map['range'])) {
+      return false;
+    }
+    if (!map.containsKey('newText') || !map['newText'] is String) {
+      return false;
+    }
+    const validFieldNames = ['range', 'newText'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// General parameters to unregister a capability.
 class Unregistration {
-  Unregistration(this.id, this.method);
+  Unregistration(this.id, this.method) {
+    if (id == null) {
+      throw 'id is required but was not provided';
+    }
+    if (method == null) {
+      throw 'method is required but was not provided';
+    }
+  }
+  factory Unregistration.fromJson(Map<String, dynamic> json) {
+    final id = json['id'];
+    final method = json['method'];
+    return new Unregistration(id, method);
+  }
 
-  /// The id used to unregister the request or notification. Usually an id
-  /// provided during the register request.
+  /// The id used to unregister the request or
+  /// notification. Usually an id provided during
+  /// the register request.
   final String id;
 
   /// The method / capability to unregister for.
@@ -3186,10 +6444,36 @@
     __result['method'] = method ?? (throw 'method is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('id') || !map['id'] is String) {
+      return false;
+    }
+    if (!map.containsKey('method') || !map['method'] is String) {
+      return false;
+    }
+    const validFieldNames = ['id', 'method'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class UnregistrationParams {
-  UnregistrationParams(this.unregisterations);
+  UnregistrationParams(this.unregisterations) {
+    if (unregisterations == null) {
+      throw 'unregisterations is required but was not provided';
+    }
+  }
+  factory UnregistrationParams.fromJson(Map<String, dynamic> json) {
+    final unregisterations = json['unregisterations']
+        ?.map((item) => new Unregistration.fromJson(item))
+        ?.cast<Unregistration>()
+        ?.toList();
+    return new UnregistrationParams(unregisterations);
+  }
 
   final List<Unregistration> unregisterations;
 
@@ -3199,22 +6483,52 @@
         (throw 'unregisterations is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('unregisterations') ||
+        !(map['unregisterations'] is List &&
+            (map['unregisterations'].length == 0 ||
+                map['unregisterations']
+                    .every((item) => Unregistration.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['unregisterations'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class VersionedTextDocumentIdentifier implements TextDocumentIdentifier {
-  VersionedTextDocumentIdentifier(this.version, this.uri);
+  VersionedTextDocumentIdentifier(this.version, this.uri) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+  }
+  factory VersionedTextDocumentIdentifier.fromJson(Map<String, dynamic> json) {
+    final version = json['version'];
+    final uri = json['uri'];
+    return new VersionedTextDocumentIdentifier(version, uri);
+  }
 
   /// The text document's URI.
   final String uri;
 
-  /// The version number of this document. If a versioned text document
-  /// identifier is sent from the server to the client and the file is not open
-  /// in the editor (the server has not received an open notification before)
-  /// the server can send `null` to indicate that the version is known and the
-  /// content on disk is the truth (as speced with document content ownership).
+  /// The version number of this document. If a
+  /// versioned text document identifier is sent
+  /// from the server to the client and the file is
+  /// not open in the editor (the server has not
+  /// received an open notification before) the
+  /// server can send `null` to indicate that the
+  /// version is known and the content on disk is
+  /// the truth (as speced with document content
+  /// ownership).
   ///
-  /// The version number of a document will increase after each change,
-  /// including undo/redo. The number doesn't need to be consecutive.
+  /// The version number of a document will increase
+  /// after each change, including undo/redo. The
+  /// number doesn't need to be consecutive.
   final num version;
 
   Map<String, dynamic> toJson() {
@@ -3223,13 +6537,39 @@
     __result['uri'] = uri ?? (throw 'uri is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('version') || !map['version'] is num) {
+      return false;
+    }
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    const validFieldNames = ['version', 'uri'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class WatchKind {
   const WatchKind._(this._value);
+  const WatchKind.fromJson(this._value);
 
   final Object _value;
 
+  static bool canParse(Object obj) {
+    switch (obj) {
+      case 1:
+      case 2:
+      case 4:
+        return true;
+    }
+    return false;
+  }
+
   /// Interested in create events.
   static const Create = const WatchKind._(1);
 
@@ -3250,9 +6590,23 @@
   bool operator ==(o) => o is WatchKind && o._value == _value;
 }
 
-/// The parameters send in a will save text document notification.
+/// The parameters send in a will save text
+/// document notification.
 class WillSaveTextDocumentParams {
-  WillSaveTextDocumentParams(this.textDocument, this.reason);
+  WillSaveTextDocumentParams(this.textDocument, this.reason) {
+    if (textDocument == null) {
+      throw 'textDocument is required but was not provided';
+    }
+    if (reason == null) {
+      throw 'reason is required but was not provided';
+    }
+  }
+  factory WillSaveTextDocumentParams.fromJson(Map<String, dynamic> json) {
+    final textDocument =
+        new TextDocumentIdentifier.fromJson(json['textDocument']);
+    final reason = json['reason'];
+    return new WillSaveTextDocumentParams(textDocument, reason);
+  }
 
   /// The 'TextDocumentSaveReason'.
   final num reason;
@@ -3267,26 +6621,57 @@
     __result['reason'] = reason ?? (throw 'reason is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('textDocument') ||
+        !TextDocumentIdentifier.canParse(map['textDocument'])) {
+      return false;
+    }
+    if (!map.containsKey('reason') || !map['reason'] is num) {
+      return false;
+    }
+    const validFieldNames = ['textDocument', 'reason'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// Workspace specific client capabilities.
 class WorkspaceClientCapabilities {
   WorkspaceClientCapabilities(this.applyEdit, this.documentChanges,
       this.resourceOperations, this.failureHandling);
+  factory WorkspaceClientCapabilities.fromJson(Map<String, dynamic> json) {
+    final applyEdit = json['applyEdit'];
+    final documentChanges = json['documentChanges'];
+    final resourceOperations = json['resourceOperations']
+        ?.map((item) => new ResourceOperationKind.fromJson(item))
+        ?.cast<ResourceOperationKind>()
+        ?.toList();
+    final failureHandling =
+        new FailureHandlingKind.fromJson(json['failureHandling']);
+    return new WorkspaceClientCapabilities(
+        applyEdit, documentChanges, resourceOperations, failureHandling);
+  }
 
-  /// The client supports applying batch edits to the workspace by supporting
-  /// the request 'workspace/applyEdit'
+  /// The client supports applying batch edits to
+  /// the workspace by supporting the request
+  /// 'workspace/applyEdit'
   final bool applyEdit;
 
-  /// The client supports versioned document changes in `WorkspaceEdit`s
+  /// The client supports versioned document
+  /// changes in `WorkspaceEdit`s
   final bool documentChanges;
 
-  /// The failure handling strategy of a client if applying the workspace edit
-  /// failes.
+  /// The failure handling strategy of a client if
+  /// applying the workspace edit failes.
   final FailureHandlingKind failureHandling;
 
-  /// The resource operations the client supports. Clients should at least
-  /// support 'create', 'rename' and 'delete' files and folders.
+  /// The resource operations the client supports.
+  /// Clients should at least support 'create',
+  /// 'rename' and 'delete' files and folders.
   final List<ResourceOperationKind> resourceOperations;
 
   Map<String, dynamic> toJson() {
@@ -3305,27 +6690,56 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = [
+      'applyEdit',
+      'documentChanges',
+      'resourceOperations',
+      'failureHandling'
+    ];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class WorkspaceEdit {
   WorkspaceEdit(this.changes, this.documentChanges);
+  factory WorkspaceEdit.fromJson(Map<String, dynamic> json) {
+    final changes = json['changes'];
+    final documentChanges = json['documentChanges']
+        ?.map((item) => item)
+        ?.cast<FileOperation>()
+        ?.toList();
+    return new WorkspaceEdit(changes, documentChanges);
+  }
 
   /// Holds changes to existing resources.
   final Map<String, List<TextEdit>> changes;
 
   /// Depending on the client capability
-  /// `workspace.workspaceEdit.resourceOperations` document changes are either
-  /// an array of `TextDocumentEdit`s to express changes to n different text
-  /// documents where each text document edit addresses a specific version of a
-  /// text document. Or it can contain above `TextDocumentEdit`s mixed with
-  /// create, rename and delete file / folder operations.
+  /// `workspace.workspaceEdit.resourceOperations`
+  /// document changes are either an array of
+  /// `TextDocumentEdit`s to express changes to n
+  /// different text documents where each text
+  /// document edit addresses a specific version
+  /// of a text document. Or it can contain above
+  /// `TextDocumentEdit`s mixed with create,
+  /// rename and delete file / folder operations.
   ///
-  /// Whether a client supports versioned document edits is expressed via
-  /// `workspace.workspaceEdit.documentChanges` client capability.
+  /// Whether a client supports versioned document
+  /// edits is expressed via
+  /// `workspace.workspaceEdit.documentChanges`
+  /// client capability.
   ///
-  /// If a client neither supports `documentChanges` nor
-  /// `workspace.workspaceEdit.resourceOperations` then only plain `TextEdit`s
-  /// using the `changes` property are supported.
+  /// If a client neither supports
+  /// `documentChanges` nor
+  /// `workspace.workspaceEdit.resourceOperations`
+  /// then only plain `TextEdit`s using the
+  /// `changes` property are supported.
   final List<FileOperation> documentChanges;
 
   Map<String, dynamic> toJson() {
@@ -3338,15 +6752,38 @@
     }
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    const validFieldNames = ['changes', 'documentChanges'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 class WorkspaceFolder {
-  WorkspaceFolder(this.uri, this.name);
+  WorkspaceFolder(this.uri, this.name) {
+    if (uri == null) {
+      throw 'uri is required but was not provided';
+    }
+    if (name == null) {
+      throw 'name is required but was not provided';
+    }
+  }
+  factory WorkspaceFolder.fromJson(Map<String, dynamic> json) {
+    final uri = json['uri'];
+    final name = json['name'];
+    return new WorkspaceFolder(uri, name);
+  }
 
-  /// The name of the workspace folder. Defaults to the uri's basename.
+  /// The name of the workspace folder. Defaults
+  /// to the uri's basename.
   final String name;
 
-  /// The associated URI for this workspace folder.
+  /// The associated URI for this workspace
+  /// folder.
   final String uri;
 
   Map<String, dynamic> toJson() {
@@ -3355,11 +6792,44 @@
     __result['name'] = name ?? (throw 'name is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('uri') || !map['uri'] is String) {
+      return false;
+    }
+    if (!map.containsKey('name') || !map['name'] is String) {
+      return false;
+    }
+    const validFieldNames = ['uri', 'name'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// The workspace folder change event.
 class WorkspaceFoldersChangeEvent {
-  WorkspaceFoldersChangeEvent(this.added, this.removed);
+  WorkspaceFoldersChangeEvent(this.added, this.removed) {
+    if (added == null) {
+      throw 'added is required but was not provided';
+    }
+    if (removed == null) {
+      throw 'removed is required but was not provided';
+    }
+  }
+  factory WorkspaceFoldersChangeEvent.fromJson(Map<String, dynamic> json) {
+    final added = json['added']
+        ?.map((item) => new WorkspaceFolder.fromJson(item))
+        ?.cast<WorkspaceFolder>()
+        ?.toList();
+    final removed = json['removed']
+        ?.map((item) => new WorkspaceFolder.fromJson(item))
+        ?.cast<WorkspaceFolder>()
+        ?.toList();
+    return new WorkspaceFoldersChangeEvent(added, removed);
+  }
 
   /// The array of added workspace folders
   final List<WorkspaceFolder> added;
@@ -3374,11 +6844,42 @@
         removed ?? (throw 'removed is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('added') ||
+        !(map['added'] is List &&
+            (map['added'].length == 0 ||
+                map['added']
+                    .every((item) => WorkspaceFolder.canParse(item))))) {
+      return false;
+    }
+    if (!map.containsKey('removed') ||
+        !(map['removed'] is List &&
+            (map['removed'].length == 0 ||
+                map['removed']
+                    .every((item) => WorkspaceFolder.canParse(item))))) {
+      return false;
+    }
+    const validFieldNames = ['added', 'removed'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
 
 /// The parameters of a Workspace Symbol Request.
 class WorkspaceSymbolParams {
-  WorkspaceSymbolParams(this.query);
+  WorkspaceSymbolParams(this.query) {
+    if (query == null) {
+      throw 'query is required but was not provided';
+    }
+  }
+  factory WorkspaceSymbolParams.fromJson(Map<String, dynamic> json) {
+    final query = json['query'];
+    return new WorkspaceSymbolParams(query);
+  }
 
   /// A non-empty query string
   final String query;
@@ -3388,4 +6889,16 @@
     __result['query'] = query ?? (throw 'query is required but was not set');
     return __result;
   }
+
+  static bool canParse(Object obj) {
+    if (!obj is Map<String, dynamic>) {
+      return false;
+    }
+    final map = obj as Map<String, dynamic>;
+    if (!map.containsKey('query') || !map['query'] is String) {
+      return false;
+    }
+    const validFieldNames = ['query'];
+    return map.keys.every((k) => validFieldNames.contains(k));
+  }
 }
diff --git a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
index 6dd189a..b6f1909 100644
--- a/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
+++ b/pkg/analysis_server/lib/lsp_protocol/protocol_special.dart
@@ -16,11 +16,19 @@
       : _t1 = null,
         _which = 2;
 
+  @override
+  get hashCode => map((t) => t.hashCode, (t) => t.hashCode);
+
+  bool operator ==(o) => o is Either2<T1, T2> && o._t1 == _t1 && o._t2 == _t2;
+
   T map<T>(T Function(T1) f1, T Function(T2) f2) {
     return _which == 1 ? f1(_t1) : f2(_t2);
   }
 
   Object toJson() => map(id, id);
+
+  /// Checks whether the value of the union equals the supplied value.
+  bool valueEquals(o) => map((t) => t == o, (t) => t == o);
 }
 
 class Either3<T1, T2, T3> {
@@ -42,6 +50,12 @@
         _t2 = null,
         _which = 3;
 
+  @override
+  get hashCode => map((t) => t.hashCode, (t) => t.hashCode, (t) => t.hashCode);
+
+  bool operator ==(o) =>
+      o is Either3<T1, T2, T3> && o._t1 == _t1 && o._t2 == _t2 && o._t3 == _t3;
+
   T map<T>(T Function(T1) f1, T Function(T2) f2, T Function(T3) f3) {
     switch (_which) {
       case 1:
@@ -56,6 +70,9 @@
   }
 
   Object toJson() => map(id, id, id);
+
+  /// Checks whether the value of the union equals the supplied value.
+  bool valueEquals(o) => map((t) => t == o, (t) => t == o, (t) => t == o);
 }
 
 class Either4<T1, T2, T3, T4> {
@@ -86,6 +103,17 @@
         _t3 = null,
         _which = 4;
 
+  @override
+  get hashCode => map((t) => t.hashCode, (t) => t.hashCode, (t) => t.hashCode,
+      (t) => t.hashCode);
+
+  bool operator ==(o) =>
+      o is Either4<T1, T2, T3, T4> &&
+      o._t1 == _t1 &&
+      o._t2 == _t2 &&
+      o._t3 == _t3 &&
+      o._t4 == _t4;
+
   T map<T>(T Function(T1) f1, T Function(T2) f2, T Function(T3) f3,
       T Function(T4) f4) {
     switch (_which) {
@@ -103,6 +131,10 @@
   }
 
   Object toJson() => map(id, id, id, id);
+
+  /// Checks whether the value of the union equals the supplied value.
+  bool valueEquals(o) =>
+      map((t) => t == o, (t) => t == o, (t) => t == o, (t) => t == o);
 }
 
 class FileOperation {}
diff --git a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
index 70be648..646d49a 100644
--- a/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
+++ b/pkg/analysis_server/test/tool/lsp_spec/json_test.dart
@@ -17,7 +17,7 @@
       expect(json.encode(_string.toJson()), equals('"Test"'));
     });
 
-    test('returns correct output for types unions', () {
+    test('returns correct output for union types', () {
       final message = new RequestMessage(new Either2.t1(1), "test", "test");
       String output = json.encode(message.toJson());
       expect(output, equals('{"id":1,"method":"test","jsonrpc":"test"}'));
@@ -63,7 +63,7 @@
       expect(output, equals(expected));
     });
 
-    test('enums serialise to their underlying values', () {
+    test('serialises enums to their underlying values', () {
       final foldingRange =
           new FoldingRange(1, 2, 3, 4, FoldingRangeKind.Comment);
       final output = json.encode(foldingRange.toJson());
@@ -78,4 +78,54 @@
       expect(output, equals(expected));
     });
   });
+
+  group('fromJson', () {
+    test('parses JSON for types with unions (left side)', () {
+      final input = '{"id":1,"method":"test","jsonrpc":"test"}';
+      final message = new RequestMessage.fromJson(jsonDecode(input));
+      expect(message.id, equals(new Either2<num, String>.t1(1)));
+      expect(message.id.valueEquals(1), isTrue);
+      expect(message.jsonrpc, "test");
+      expect(message.method, "test");
+    });
+
+    test('parses JSON for types with unions (right side)', () {
+      final input = '{"id":"one","method":"test","jsonrpc":"test"}';
+      final message = new RequestMessage.fromJson(jsonDecode(input));
+      expect(message.id, equals(new Either2<num, String>.t2("one")));
+      expect(message.id.valueEquals("one"), isTrue);
+      expect(message.jsonrpc, "test");
+      expect(message.method, "test");
+    });
+  });
+
+  test('objects with lists and enums can round-trip through to json and back',
+      () {
+    final obj = new ClientCapabilities(
+        new WorkspaceClientCapabilities(
+            true,
+            false,
+            [ResourceOperationKind.Create, ResourceOperationKind.Delete],
+            FailureHandlingKind.Undo),
+        new TextDocumentClientCapabilities(true, false, true, false),
+        null);
+    final String json = jsonEncode(obj);
+    final restoredObj = new ClientCapabilities.fromJson(jsonDecode(json));
+
+    expect(restoredObj.workspace.applyEdit, equals(obj.workspace.applyEdit));
+    expect(restoredObj.workspace.documentChanges,
+        equals(obj.workspace.documentChanges));
+    expect(restoredObj.workspace.resourceOperations,
+        equals(obj.workspace.resourceOperations));
+    expect(restoredObj.workspace.failureHandling,
+        equals(obj.workspace.failureHandling));
+    expect(restoredObj.textDocument.didSave, equals(obj.textDocument.didSave));
+    expect(restoredObj.textDocument.dynamicRegistration,
+        equals(obj.textDocument.dynamicRegistration));
+    expect(
+        restoredObj.textDocument.willSave, equals(obj.textDocument.willSave));
+    expect(restoredObj.textDocument.willSaveWaitUntil,
+        equals(obj.textDocument.willSaveWaitUntil));
+    expect(restoredObj.experimental, equals(obj.experimental));
+  });
 }
diff --git a/pkg/analysis_server/test/tool/lsp_spec/typescript_to_dart_test.dart b/pkg/analysis_server/test/tool/lsp_spec/typescript_to_dart_test.dart
deleted file mode 100644
index 9cd5290..0000000
--- a/pkg/analysis_server/test/tool/lsp_spec/typescript_to_dart_test.dart
+++ /dev/null
@@ -1,84 +0,0 @@
-// Copyright (c) 2018, the Dart project authors. Please see the AUTHORS file
-// for details. All rights reserved. Use of this source code is governed by a
-// BSD-style license that can be found in the LICENSE file.
-
-import 'package:test/test.dart';
-
-import '../../../tool/lsp_spec/codegen_dart.dart';
-import '../../../tool/lsp_spec/typescript.dart';
-
-main() {
-  group('typescript converts to dart', () {
-    void convertAndCompare(String input, String expectedOutput) {
-      final String output = generateDartForTypes(extractTypes(input));
-      expect(output.trim(), equals(expectedOutput.trim()));
-    }
-
-    // TODO(dantup): These types are missing constructors, toJson, fromJson, etc.
-
-    test('for an interface', () {
-      final String input = '''
-/**
- * Some options.
- */
-export interface SomeOptions {
-	/**
-	 * Options used by something.
-	 */
-	options?: OptionKind[];
-}
-    ''';
-      final String expectedOutput = '''
-/// Some options.
-class SomeOptions {
-  SomeOptions(this.options);
-
-  /// Options used by something.
-  final List<OptionKind> options;
-}
-    ''';
-      convertAndCompare(input, expectedOutput);
-    });
-
-    test('uses aliases types in place of aliases', () {
-      final String input = '''
-type DocumentUri = string;
-
-export interface SomeDocumentThing {
-	uris: DocumentUri[];
-}
-    ''';
-      final String expectedOutput = '''
-class SomeDocumentThing {
-  SomeDocumentThing(this.uris);
-
-  final List<String /*DocumentUri*/ > uris;
-}
-    ''';
-      convertAndCompare(input, expectedOutput);
-    });
-
-    test('outputs references in comments in the correct format', () {
-      final String input = '''
-export interface One {
-}
-
-/**
- *  This may refer to [a one](#One) or just [One](#One).
- */
-export interface Two {
-}
-    ''';
-      final String expectedOutput = '''
-class One {}
-
-/// This may refer to a one ([One]) or just [One].
-class Two {}
-    ''';
-      convertAndCompare(input, expectedOutput);
-    });
-    // Skip these tests while toJson methods/etc. are in progress and the generated
-    // code changes frequently.
-    // TODO(dantup): Re-enable these.
-  }, skip: true);
-}
diff --git a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
index 3018259..065a31b 100644
--- a/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
+++ b/pkg/analysis_server/tool/lsp_spec/codegen_dart.dart
@@ -8,6 +8,7 @@
 
 final formatter = new DartFormatter();
 Map<String, Interface> _interfaces = {};
+Map<String, Namespace> _namespaces = {};
 Map<String, TypeAlias> _typeAliases = {};
 
 String generateDartForTypes(List<ApiItem> types) {
@@ -18,6 +19,9 @@
   types
       .whereType<Interface>()
       .forEach((interface) => _interfaces[interface.name] = interface);
+  types
+      .whereType<Namespace>()
+      .forEach((namespace) => _namespaces[namespace.name] = namespace);
   final buffer = new IndentableStringBuffer();
   _getSorted(types).forEach((t) => _writeType(buffer, t));
   final formattedCode = _formatCode(buffer.toString());
@@ -48,6 +52,10 @@
       .toList();
 }
 
+String _getListType(String type) {
+  return type.substring('List<'.length, type.length - 1);
+}
+
 /// Returns a copy of the list sorted by name.
 List<ApiItem> _getSorted(List<ApiItem> items) {
   final sortedList = items.toList();
@@ -55,6 +63,31 @@
   return sortedList;
 }
 
+List<String> _getUnionTypes(String type) {
+  return type
+      .substring('EitherX<'.length, type.length - 1)
+      .split(',')
+      .map((s) => s.trim())
+      .toList();
+}
+
+bool _isList(String type) {
+  return type.startsWith('List<') && type.endsWith('>');
+}
+
+bool _isLiteral(String type) {
+  const literals = ['num', 'String', 'bool'];
+  return literals.contains(type);
+}
+
+bool _isSpecType(String type) {
+  return _interfaces.containsKey(type) || _namespaces.containsKey(type);
+}
+
+bool _isUnion(String type) {
+  return type.startsWith('Either') && type.endsWith('>');
+}
+
 /// Maps reserved words and identifiers that cause issues in field names.
 String _makeValidIdentifier(String identifier) {
   // The SymbolKind class has uses these names which cause issues for code that
@@ -133,6 +166,42 @@
   }
 }
 
+void _writeCanParseMethod(IndentableStringBuffer buffer, Interface interface) {
+  buffer
+    ..writeIndentedln('static bool canParse(Object obj) {')
+    ..indent()
+    ..writeIndentedln('if (!obj is Map<String, dynamic>) {')
+    ..indent()
+    ..writeIndentedln('return false;')
+    ..outdent()
+    ..writeIndentedln('}')
+    ..writeIndentedln('final map = obj as Map<String, dynamic>;');
+  // In order to consider this valid for parsing, all fields that may not be
+  // undefined must be present and also type check for the correct type.
+  // If these pass, there must also be no fields that are not defined for this
+  // type.
+  final allFields = _getAllFields(interface);
+  final requiredFields = allFields.where((f) => !f.allowsUndefined);
+  for (var field in requiredFields) {
+    buffer.writeIndented("if (!map.containsKey('${field.name}') || !");
+    _writeTypeCheckCondition(
+        buffer, "map['${field.name}']", _mapType(field.types));
+    buffer
+      ..writeln(') {')
+      ..indent()
+      ..writeIndentedln('return false;')
+      ..outdent()
+      ..writeIndentedln('}');
+  }
+  final fieldNames = allFields.map((f) => f.name).join("', '");
+  buffer.writeIndentedln("const validFieldNames = ['$fieldNames'];");
+  buffer
+    ..writeIndentedln(
+        'return map.keys.every((k) => validFieldNames.contains(k));')
+    ..outdent()
+    ..writeIndentedln('}');
+}
+
 void _writeConst(IndentableStringBuffer buffer, Const cons) {
   _writeDocCommentsAndAnnotations(buffer, cons);
   buffer.writeIndentedln('static const ${cons.name} = ${cons.value};');
@@ -146,7 +215,28 @@
   buffer
     ..writeIndented('${interface.name}(')
     ..write(allFields.map((field) => 'this.${field.name}').join(', '))
-    ..writeln(');');
+    ..write(')');
+  final fieldsWithValidation =
+      allFields.where((f) => !f.allowsNull && !f.allowsUndefined).toList();
+  if (fieldsWithValidation.isNotEmpty) {
+    buffer
+      ..writeIndentedln(' {')
+      ..indent();
+    for (var field in fieldsWithValidation) {
+      buffer
+        ..writeIndentedln('if (${field.name} == null) {')
+        ..indent()
+        ..writeIndentedln(
+            "throw '${field.name} is required but was not provided';")
+        ..outdent()
+        ..writeIndentedln('}');
+    }
+    buffer
+      ..outdent()
+      ..writeIndentedln('}');
+  } else {
+    buffer.writeln(';');
+  }
 }
 
 void _writeDocCommentsAndAnnotations(
@@ -158,7 +248,7 @@
   comment = _rewriteCommentReference(comment);
   Iterable<String> lines = comment.split('\n');
   // Wrap at 80 - 4 ('/// ') - indent characters.
-  lines = _wrapLines(lines, 80 - 4 - buffer.totalIndent);
+  lines = _wrapLines(lines, (80 - 4 - buffer.totalIndent).clamp(0, 80));
   lines.forEach((l) => buffer.writeIndentedln('/// $l'.trim()));
   if (item.isDeprecated) {
     buffer.writeIndentedln('@core.deprecated');
@@ -171,9 +261,25 @@
     ..writeln('class ${namespace.name} {')
     ..indent()
     ..writeIndentedln('const ${namespace.name}._(this._value);')
+    ..writeIndentedln('const ${namespace.name}.fromJson(this._value);')
     ..writeln()
     ..writeIndentedln('final Object _value;')
-    ..writeln();
+    ..writeln()
+    ..writeIndentedln('static bool canParse(Object obj) {')
+    ..indent()
+    ..writeIndentedln('switch (obj) {')
+    ..indent();
+  namespace.members.whereType<Const>().forEach((cons) {
+    buffer..writeIndentedln('case ${cons.value}:');
+  });
+  buffer
+    ..indent()
+    ..writeIndentedln('return true;')
+    ..outdent()
+    ..writeIndentedln('}')
+    ..writeIndentedln('return false;')
+    ..outdent()
+    ..writeIndentedln('}');
   namespace.members.whereType<Const>().forEach((cons) {
     _writeDocCommentsAndAnnotations(buffer, cons);
     buffer
@@ -203,6 +309,70 @@
     ..writeln(' ${field.name};');
 }
 
+void _writeFromJsonCode(
+    IndentableStringBuffer buffer, List<String> types, String valueCode) {
+  final type = _mapType(types);
+  if (_isLiteral(type)) {
+    buffer.write("$valueCode");
+  } else if (_isSpecType(type)) {
+    // Our own types have fromJson() constructors we can call.
+    buffer.write("new $type.fromJson($valueCode)");
+  } else if (_isList(type)) {
+    // Lists need to be mapped so we can recursively call (they may need fromJson).
+    buffer.write("$valueCode?.map((item) => ");
+    final listType = _getListType(type);
+    _writeFromJsonCode(buffer, [listType], 'item');
+    buffer.write(')?.cast<$listType>()?.toList()');
+  } else if (_isUnion(type)) {
+    _writeFromJsonCodeForUnion(buffer, types, valueCode);
+  } else {
+    buffer.write("$valueCode");
+  }
+}
+
+void _writeFromJsonCodeForUnion(
+    IndentableStringBuffer buffer, List<String> types, String valueCode) {
+  final unionTypeName = _mapType(types);
+  // Write a check against each type, eg.:
+  // x is y ? new Either.tx(x) : (...)
+  for (var i = 0; i < types.length; i++) {
+    final dartType = _mapType([types[i]]);
+
+    _writeTypeCheckCondition(buffer, valueCode, dartType);
+    buffer.write(' ? new $unionTypeName.t${i + 1}(');
+    _writeFromJsonCode(buffer, [dartType], valueCode); // Call recursively!
+    buffer.write(') : (');
+  }
+  // Fill the final parens with a throw because if we fell through all of the
+  // cases then the value we had didn't match any of the types in the union.
+  buffer
+      .write("throw '''\${$valueCode} was not one of (${types.join(', ')})'''");
+  buffer.write(')' * types.length);
+}
+
+void _writeFromJsonConstructor(
+    IndentableStringBuffer buffer, Interface interface) {
+  final allFields = _getAllFields(interface);
+  if (allFields.isEmpty) {
+    return;
+  }
+  buffer
+    ..writeIndentedln(
+        'factory ${interface.name}.fromJson(Map<String, dynamic> json) {')
+    ..indent();
+  for (final field in allFields) {
+    buffer.writeIndented('final ${field.name} = ');
+    _writeFromJsonCode(buffer, field.types, "json['${field.name}']");
+    buffer.writeln(';');
+  }
+  buffer
+    ..writeIndented('return new ${interface.name}(')
+    ..write(allFields.map((field) => '${field.name}').join(', '))
+    ..writeln(');')
+    ..outdent()
+    ..write('}');
+}
+
 void _writeInterface(IndentableStringBuffer buffer, Interface interface) {
   _writeDocCommentsAndAnnotations(buffer, interface);
 
@@ -214,6 +384,7 @@
     ..writeln('{')
     ..indent();
   _writeConstructor(buffer, interface);
+  _writeFromJsonConstructor(buffer, interface);
   // Handle Consts and Fields separately, since we need to include superclass
   // Fields.
   final consts = interface.members.whereType<Const>().toList();
@@ -224,7 +395,7 @@
   _writeMembers(buffer, fields);
   buffer.writeln();
   _writeToJsonMethod(buffer, interface);
-  // TODO(dantup): Generate fromJson()
+  _writeCanParseMethod(buffer, interface);
   buffer
     ..outdent()
     ..writeIndentedln('}')
@@ -324,6 +495,37 @@
   }
 }
 
+void _writeTypeCheckCondition(
+    IndentableStringBuffer buffer, String valueCode, String dartType) {
+  if (dartType == 'dynamic') {
+    buffer.write('true');
+  } else if (_isLiteral(dartType)) {
+    buffer.write('$valueCode is $dartType');
+  } else if (_isSpecType(dartType)) {
+    buffer.write('$dartType.canParse($valueCode)');
+  } else if (_isList(dartType)) {
+    // TODO(dantup): If we're happy to assume we never have two lists in a union
+    // we could simplify this to '$valueCode is List'.
+    buffer.write(
+        '($valueCode is List && ($valueCode.length == 0 || $valueCode.every((item) => ');
+    _writeTypeCheckCondition(buffer, 'item', _getListType(dartType));
+    buffer.write(')))');
+  } else if (_isUnion(dartType)) {
+    // To type check a union, we just recursively check against each of its types.
+    final unionTypes = _getUnionTypes(dartType);
+    buffer.write('(');
+    for (var i = 0; i < unionTypes.length; i++) {
+      if (i != 0) {
+        buffer.write(' || ');
+      }
+      _writeTypeCheckCondition(buffer, valueCode, _mapType([unionTypes[i]]));
+    }
+    buffer.write(')');
+  } else {
+    throw 'Unable to type check $valueCode against $dartType';
+  }
+}
+
 class IndentableStringBuffer extends StringBuffer {
   int _indentLevel = 0;
   int _indentSpaces = 2;