// The method that is called to invoke our task. public override bool Execute() { if (InputFiles == null) { return(true); } var outCodeItems = new List <TaskItem>(); var outResItems = new List <TaskItem>(); string generatorName = typeof(JsonResourceGenerator).FullName; string generatorVersion = typeof(JsonResourceGenerator).GetTypeInfo().Assembly.GetName().Version.ToString(); // loop over all the .resj files we were given foreach (var iFile in InputFiles) { var fn = Path.GetFileNameWithoutExtension(iFile.ItemSpec); var culturePart = Path.GetExtension(fn); var hasCulture = false; if (culturePart?.Length > 1) { culturePart = culturePart.Substring(1); hasCulture = Culture.IsValidCulture(culturePart); } var fileDir = Path.GetDirectoryName(iFile.ItemSpec); var filePath = iFile.ItemSpec; var logger = new FileErrorLogger(this, filePath); // load the Json from the file var text = File.ReadAllText(filePath); var json = new JsonReader(new StringReader(text), logger.JsonError); var doc = JsonDocument.Load(json); if (doc.RootNode == null) { logger.ParseError("Failed to parse json text.", doc); continue; } if (doc.RootNode.NodeType != JsonNodeType.Object) { logger.ParseError("Expected object as root node.", doc.RootNode); continue; } var root = (JsonObject)doc.RootNode; var resName = iFile.GetMetadata("ResourceName"); if (string.IsNullOrEmpty(resName)) { resName = Path.GetFileNameWithoutExtension(iFile.ItemSpec); } var resourceName = resName + ".resources"; Directory.CreateDirectory(OutputPath); // write the generated C# code and resources file. if (!hasCulture) { var codeFile = Path.Combine(OutputPath, iFile.ItemSpec + ".g.cs"); var access = iFile.GetMetadata("AccessModifier"); var noCodeGen = StringComparer.OrdinalIgnoreCase.Equals(access, AccessNoCodeGen); if (!noCodeGen) { var isPublic = StringComparer.OrdinalIgnoreCase.Equals(access, AccessPublic); var ns = iFile.GetMetadata("Namespace"); string className = Path.GetFileNameWithoutExtension(iFile.ItemSpec); outCodeItems.Add(new TaskItem(codeFile)); Directory.CreateDirectory(Path.GetDirectoryName(codeFile)); using (var oStream = new FileStream(codeFile, FileMode.Create)) using (var w = new StreamWriter(oStream)) { if (!string.IsNullOrEmpty(ns)) { w.WriteLine($"namespace {ns} {{"); w.WriteLine(); } // very simplistic resource accessor class mostly duplicated from resx output. w.Write($@"using global::System.Reflection; /// <summary> /// A strongly-typed resource class, for looking up localized strings, etc. /// </summary> [global::System.Diagnostics.DebuggerNonUserCode()] [global::System.CodeDom.Compiler.GeneratedCodeAttribute(""{generatorName}"", ""{generatorVersion}"")] {(isPublic ? "public " : string.Empty)}static partial class {className} {{ static global::System.Resources.ResourceManager rm; static global::System.Globalization.CultureInfo resourceCulture; static global::System.Resources.ResourceManager ResourceManager {{ get {{ if (object.ReferenceEquals(rm, null)) {{ rm = new global::System.Resources.ResourceManager(""{resName}"", typeof({className}).GetTypeInfo().Assembly); }} return rm; }} }} static global::System.Globalization.CultureInfo Culture {{ get {{ return resourceCulture; }} set {{ resourceCulture = value; }} }} "); foreach (var section in root.Keys) { var value = root[section]; switch (section) { case "Strings": case "Files": if (value.NodeType == JsonNodeType.Object) { var obj = (JsonObject)value; foreach (var item in obj) { string comment = null; if (section == "Strings") { // https://stackoverflow.com/a/19498780 string stringValue = new XText(((JsonString)item.Value).Value).ToString(); comment = $"Looks up a localized string similar to {stringValue.Substring(0, Math.Min(stringValue.Length, 100))}"; } else if (section == "Files") { comment = $"Looks up a {item.Key} text file"; } if (comment != null) { IEnumerable <string> commentLines = comment.Split(new[] { "\r", "\n", "\r\n" }, StringSplitOptions.None); w.Write($@" /// <summary> /// {string.Join(Environment.NewLine + " /// ", commentLines)}. /// </summary>"); } w.Write($@" public static string {item.Key} {{ get {{ return ResourceManager.GetString(""{item.Key}"", resourceCulture); }} }} "); } } else { //logger.ParseError("Expected Json object.", value); } break; default: //logger.ParseError("Unexpected property.", value); break; } } w.WriteLine("}"); if (!string.IsNullOrEmpty(ns)) { w.WriteLine(); w.WriteLine("}"); } } } } // prepare the generated files we are about to write. var resFile = Path.Combine(OutputPath, resourceName); var resItem = new TaskItem(resFile); resItem.SetMetadata("LogicalName", resName + ".resources"); resItem.SetMetadata("ManifestResourceName", resourceName); resItem.SetMetadata("OutputResource", resourceName); resItem.SetMetadata("WithCulture", hasCulture ? "true" : "false"); resItem.SetMetadata("Type", "Non-Resx"); outResItems.Add(resItem); if (hasCulture) { resItem.SetMetadata("Culture", culturePart); } else { } json = new JsonReader(new StringReader(text), logger.JsonError); using (var rw = new System.Resources.ResourceWriter(resFile)) { foreach (var section in root.Keys) { var sectionNode = root[section]; var isFile = false; switch (section) { case "Strings": isFile = false; break; case "Files": isFile = true; break; default: logger.ParseError("Unexpected section.", sectionNode); continue; } if (sectionNode.NodeType != JsonNodeType.Object) { logger.ParseError("Expected json object", sectionNode); continue; } var sectionObj = (JsonObject)sectionNode; foreach (var key in sectionObj.Keys) { var str = sectionObj[key]; if (str.NodeType != JsonNodeType.String) { logger.ParseError("Expected string value", str); continue; } var strVal = (JsonString)str; string txt; if (isFile) { var textFilePath = Path.Combine(fileDir, strVal.Value); if (!File.Exists(textFilePath)) { logger.ParseError("Resource file '" + strVal.Value + "' not found.", str); continue; } try { using (var iStream = File.OpenRead(textFilePath)) using (var reader = new StreamReader(iStream)) { txt = reader.ReadToEnd(); } } catch (Exception e) { logger.ParseError(e.Message, str); continue; } } else { txt = strVal.Value; } // write our string to the resources binary rw.AddResource(key, txt); } } } } // put the artifacts we created in the output properties. OutputCode = outCodeItems.ToArray(); OutputResources = outResItems.ToArray(); return(!hasError); }