web-dev-qa-db-ja.com

.NETで文字列テンプレートを作成する良い方法は何ですか?

ユーザーに電子メール通知を送信する必要があり、管理者がメッセージ本文のテンプレート(および場合によってはヘッダーも)を提供できるようにする必要があります。

名前付きの置換文字列を提供できるstring.Formatのようなものが欲しいので、テンプレートは次のようになります。

Dear {User},

Your job finished at {FinishTime} and your file is available for download at {FileURL}.

Regards,

-- 
{Signature}

私がそれを行う最も簡単な方法は何ですか?

42
Simon

テンプレートエンジンを使用します。 StringTemplate はそれらの1つであり、多数あります。

21
Anton Gogolev

C#の新しいバージョンを使用できるユーザー向けのバージョンは次のとおりです。

// add $ at start to mark string as template
var template = $"Your job finished at {FinishTime} and your file is available for download at {FileURL}."

一列に-これは現在、完全にサポートされている言語機能です(文字列補間)。

33

「string.Format」メソッドを使用できます。

var user = GetUser();
var finishTime = GetFinishTime();
var fileUrl = GetFileUrl();
var signature = GetSignature();
string msg =
@"Dear {0},

Your job finished at {1} and your file is available for download at {2}.

Regards,

--
{3}";
msg = string.Format(msg, user, finishTime, fileUrl, signature);

将来コンテンツを変更することができ、ローカライズに適しています。

16
TcKs

SmartFormat は、すべての要件を満たす非常にシンプルなライブラリです。 「自然言語」テキストの作成に焦点を当てており、リストからデータを生成したり、条件付きロジックを適用したりするのに最適です。

構文はString.Formatと非常に似ており、非常にシンプルで簡単に習得して使用できます。ドキュメントの構文の例を次に示します。

Smart.Format("{Name}'s friends: {Friends:{Name}|, |, and}", user)
// Result: "Scott's friends: Michael, Jim, Pam, and Dwight"

ライブラリには優れたエラー処理オプションがあります(エラーを無視、エラーを出力、エラーをスロー)。明らかに、これはあなたの例に最適です。

このライブラリはオープンソースであり、簡単に拡張できるため、追加機能を使用してライブラリを拡張することもできます。

13
Scott Rippey

String.Replace(...)を使用して、最終的にすべてのキーワードを介したfor-eachで使用できます。少数のキーワードしかない場合は、次のような行にそれらを含めることができます。

string myString = template.Replace("FirstName", "John").Replace("LastName", "Smith").Replace("FinishTime", DateTime.Now.ToShortDateString());

または、もう少し強力でより多くのオプションが必要な場合は、Regex.Replace(...)を使用できます。

これを読む codeprojectの記事 どの文字列置換オプションがあなたにとって最も速いかを見るために。

10
Ovi

Benjamin Gruenbaumの答えに基づいて、C#バージョン6では、$に@を追加し、コードをほとんどそのまま使用できます(例:

var text = $@"Dear {User},

Your job finished at {FinishTime} and your file is available for download at {FileURL}.

Regards,

-- 
{Signature}
";

$は文字列補間用です。 https://docs.Microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated

@は逐語的な識別子です。 https://docs.Microsoft.com/en-us/dotnet/csharp/language-reference/tokens/verbatim

...これらを組み合わせて使用​​できます。

:o)

8
mrrrk

実際には、XSLTを使用できます。単純なXMLテンプレートを作成します。

<?xml version="1.0" encoding="utf-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:msxsl="urn:schemas-Microsoft-com:xslt" exclude-result-prefixes="msxsl">
  <xsl:template match="TETT">
    <p>
       Dear <xsl:variable name="USERNAME" select="XML_PATH" />,

       Your job finished at <xsl:variable name="FINISH_TIME" select="XML_PATH" /> and your file is available for download at <xsl:variable name="FILE_URL" select="XML_PATH" />.

       Regards,
        -- 
       <xsl:variable name="SIGNATURE" select="XML_PATH" />
    </p>
</xsl:template>

次に、変換を実行するXmlDocumentを作成します。XmlDocumentxmlDoc = new XmlDocument();

        XmlNode xmlNode = xmlDoc .CreateNode(XmlNodeType.Element, "EMAIL", null);
        XmlElement xmlElement= xmlDoc.CreateElement("USERNAME");
        xmlElement.InnerXml = username;
        xmlNode .AppendChild(xmlElement); ///repeat the same thing for all the required fields

        xmlDoc.AppendChild(xmlNode);

その後、変換を適用します。

        XPathNavigator xPathNavigator = xmlDocument.DocumentElement.CreateNavigator();
        StringBuilder sb = new StringBuilder();
        StringWriter sw = new StringWriter(sb);
        XmlTextWriter xmlWriter = new XmlTextWriter(sw);
        your_xslt_transformation.Transform(xPathNavigator, null, xmlWriter);
        return sb.ToString();
5
0100110010101

非常にシンプルな正規表現ベースのソリューション。 _\n_スタイルの単一文字エスケープシーケンスと_{Name}_スタイルの名前付き変数をサポートします。

ソース

_class Template
{
    /// <summary>Map of replacements for characters prefixed with a backward slash</summary>
    private static readonly Dictionary<char, string> EscapeChars
        = new Dictionary<char, string>
        {
            ['r'] = "\r",
            ['n'] = "\n",
            ['\\'] = "\\",
            ['{'] = "{",
        };

    /// <summary>Pre-compiled regular expression used during the rendering process</summary>
    private static readonly Regex RenderExpr = new Regex(@"\\.|{([a-z0-9_.\-]+)}",
        RegexOptions.IgnoreCase | RegexOptions.Compiled);

    /// <summary>Template string associated with the instance</summary>
    public string TemplateString { get; }

    /// <summary>Create a new instance with the specified template string</summary>
    /// <param name="TemplateString">Template string associated with the instance</param>
    public Template(string TemplateString)
    {
        if (TemplateString == null) {
            throw new ArgumentNullException(nameof(TemplateString));
        }

        this.TemplateString = TemplateString;
    }

    /// <summary>Render the template using the supplied variable values</summary>
    /// <param name="Variables">Variables that can be substituted in the template string</param>
    /// <returns>The rendered template string</returns>
    public string Render(Dictionary<string, object> Variables)
    {
        return Render(this.TemplateString, Variables);
    }

    /// <summary>Render the supplied template string using the supplied variable values</summary>
    /// <param name="TemplateString">The template string to render</param>
    /// <param name="Variables">Variables that can be substituted in the template string</param>
    /// <returns>The rendered template string</returns>
    public static string Render(string TemplateString, Dictionary<string, object> Variables)
    {
        if (TemplateString == null) {
            throw new ArgumentNullException(nameof(TemplateString));
        }

        return RenderExpr.Replace(TemplateString, Match => {
            switch (Match.Value[0]) {
                case '\\':
                    if (EscapeChars.ContainsKey(Match.Value[1])) {
                        return EscapeChars[Match.Value[1]];
                    }
                    break;

                case '{':
                    if (Variables.ContainsKey(Match.Groups[1].Value)) {
                        return Variables[Match.Groups[1].Value].ToString();
                    }
                    break;
            }

            return string.Empty;
        });
    }
}
_

使用法

_var tplStr1 = @"Hello {Name},\nNice to meet you!";
var tplStr2 = @"This {Type} \{contains} \\ some things \\n that shouldn't be rendered";
var variableValues = new Dictionary<string, object>
{
    ["Name"] = "Bob",
    ["Type"] = "string",
};

Console.Write(Template.Render(tplStr1, variableValues));
// Hello Bob,
// Nice to meet you!

var template = new Template(tplStr2);
Console.Write(template.Render(variableValues));
// This string {contains} \ some things \n that shouldn't be rendered
_

ノート

  • _\n_、_\r_、_\\_、および_\{_エスケープシーケンスのみを定義し、それらをハードコーディングしました。簡単に追加したり、消費者が定義できるようにすることができます。
  • このようなものはエンドユーザー/非プログラマーにしばしば提示されるので、私は変数名を大文字と小文字を区別しないようにしました。誤解して電話で文句を言うことができます(さらに、大文字と小文字を区別するシンボル名が必要な場合は、本当に必要なのはより良いシンボル名です)。大文字と小文字を区別するには、単に_RegexOptions.IgnoreCase_フラグを削除します。
  • 結果文字列から無効な変数名とエスケープシーケンスを削除します。それらをそのままにするには、_Match.Value_コールバックの最後に空の文字列の代わりに_Regex.Replace_を返します。例外をスローすることもできます。
  • _{var}_構文を使用しましたが、これはネイティブの補間文字列構文に干渉する可能性があります。コード内の文字列リテラルでテンプレートを定義する場合、変数の区切り文字を変更することをお勧めします。 _%var%_(regex \\.|%([a-z0-9_.\-]+)%)またはユースケースにより適した選択した他の構文。
4
DaveRandom

独自のカスタムフォーマッタを実装することをお勧めします。

方法は次のとおりです。最初に、メッセージに挿入する内容を定義するタイプを作成します。注:これは、テンプレートのユーザー部分でのみ説明します...

class JobDetails
{
    public string User 
    { 
        get;
        set; 
    }        
}

次に、簡単なカスタムフォーマッタを実装します...

class ExampleFormatter : IFormatProvider, ICustomFormatter
{
    public object GetFormat(Type formatType)
    {
        return this;
    }

    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        // make this more robust
        JobDetails job = (JobDetails)arg;

        switch (format)
        {
            case "User":
            {
                return job.User;
            }
            default:
            {
                // this should be replaced with logic to cover the other formats you need
                return String.Empty;
            }
        }
    }
}

最後に、このように使用します...

string template = "Dear {0:User}. Your job finished...";

JobDetails job = new JobDetails()
                     {
                             User = "Martin Peck"
                     };

string message = string.Format(new ExampleFormatter(), template, job);

...「親愛なるマーティン・ペック。あなたの仕事は終わりました...」というテキストを生成します。

4
Martin Peck

VB.NETでコーディングしている場合は、XMLリテラルを使用できます。 C#でコーディングしている場合は、ShartDevelopを使用して、C#コードと同じプロジェクトのVB.NETにファイルを作成できます。

1
epitka

非常に強力なものが必要な場合(実際には最も簡単な方法ではありません)、ASP.NETをホストし、それをテンプレートエンジンとして使用できます。

ASP.NETのすべての機能を使用して、メッセージの本文をフォーマットできます。

1