从JSON文件中解析C#函数

时间:2016-09-17 06:59:04

标签: c# json parsing

我正在搞乱游戏项目的实现,为了避免对项目进行硬编码,我想从外部文件中加载他们的信息和实现。

基本格式如下:

-- Items.json
[
    {
        "name": "Rusty Key",
        "shortDesc": "A rusty key.",
        "longDesc": "A slightly rusty key. You don't know what door it opens.",
        "code": "RustyKey.cs"
    }
]

或者将Use()代码直接嵌入到JSON文件中:

-- Items.json
[
    {
        "name": "Rusty Key"
        ...
        "use": "return 0;"
    }
]

JSON文件将在运行时加载并动态添加项目及其实现。

这也可能变成一种简单的脚本语言,允许玩家创建自己的项目,但是由于可能存在滥用行为,我现在不会将其发布给公众。

所以我的问题是:如何加载/解析附加的脚本文件或嵌入代码并将其附加到动态创建的对象?

2 个答案:

答案 0 :(得分:0)

创建MyLibrary.dll并添加以下代码:

namespace MyTypesNamespace
{

    public abstract class BaseItem
    {
        public abstract bool Use(object onMyObject);
    }

    public class Door
    {
        public bool IsLocked { get; set; }

        public bool Open()
        {
            if (IsLocked)
            {
                System.Console.WriteLine("Cannot open door. It is locked!");
                return false;
            }

            //Some code
            System.Console.WriteLine("Door is opened!");
            return true;
        }
    }
}

在您的主项目中,添加对MyLibrary.dll的引用和以下方法:

private static List<BaseItem> loadItems(string fromCode)
{
    CodeDomProvider codeProvider = new CSharpCodeProvider();

    // add compiler parameters
    CompilerParameters compilerParams = new CompilerParameters();
    compilerParams.CompilerOptions = "/target:library /optimize";
    compilerParams.GenerateExecutable = false;
    compilerParams.GenerateInMemory = true;
    compilerParams.IncludeDebugInformation = false;
    compilerParams.ReferencedAssemblies.Add("mscorlib.dll");
    compilerParams.ReferencedAssemblies.Add("System.dll");
    compilerParams.ReferencedAssemblies.Add("MyLibrary.dll");

    // compile the code
    CompilerResults results = codeProvider.CompileAssemblyFromSource(compilerParams, fromCode);
    var items = new List<BaseItem>();
    foreach (var itemType in results.CompiledAssembly.DefinedTypes)
    {
        ConstructorInfo ctor = itemType.GetConstructor(Type.EmptyTypes);
        object instance = ctor.Invoke(null);
        items.Add(instance as BaseItem);
    }
    return items;
}

用法:

private static void Main()
{
    string code =  loadCode();
    List<BaseItem> items = loadItems(code);

    BaseItem rustyKey = items[0];
    BaseItem unlockAnyDoor = items[1];

    Door myDoor = new Door { IsLocked = true };
    rustyKey.Use(myDoor);
    unlockAnyDoor.Use(myDoor);
    rustyKey.Use(myDoor);


    Console.ReadLine();
}

private static string loadCode()
{
    return @"
        using MyTypesNamespace;
        public class RustyKey : BaseItem
        {
            public override bool Use(object onMyObject)
            {
                var door = onMyObject as Door;

                return door.Open();
            }
        }

        public class UnlockAnyDoor : BaseItem
        {
            public override bool Use(object onMyObject)
            {
                var door = onMyObject as Door;
                door.IsLocked = false;
                System.Console.WriteLine(""Door is unlocked!"");
                return true;
            }
        }
";
}

输出:(See the output online

Cannot open door. It is locked!
Door is unlocked!
Door is opened!

编辑:

您可以通过消除代码重复来简化将来的代码供应(将从文件中读取)。设generateItemClass方法将采用classNameUse()方法的主体:

private static string generateItemClass(string className, string useBody)
{
    return $@"
        public class {className} : BaseItem
        {{
            public override bool Use(object onMyObject)
            {{
                {useBody}
            }}
        }}";
}

不要忘记为所有课程添加using MyTypesNamespace;

private static string loadCode()
{
    string namespaces = @"using MyTypesNamespace;";

    string rustyKeyClass = generateItemClass("RustyKey",
        @"var door = onMyObject as Door;
        return door.Open();");

    string unlockAnyDoorClass = generateItemClass("UnlockAnyDoor",
        @"var door = onMyObject as Door;
        door.IsLocked = false;
        System.Console.WriteLine(""Door is unlocked!"");
        return true;");

    return namespaces + rustyKeyClass + unlockAnyDoorClass;
}

答案 1 :(得分:0)

将原始代码放入JSON意味着必须获取该代码,可能会解析它,将其粘贴到某些代码shell中,编译它并检查错误。这是很多机器。 (另一个答案非常详细地说明了这一点)。

还有一个问题是,如果有人可以在那里编写任意代码,他们会,并且他们会写出一些以任意奇怪的方式破坏你的应用程序的东西,要么是造成安全漏洞,要么是没有其他一些困难的调试问题。

我认为在大多数情况下你会更好地在JSON中发出你想要的信号,例如,

-- Items.json
[
    {
        "name": "Rusty Key"
        ...
        "result": "0"
    }
]

现在程序逻辑读取JSON文件,存储&lt;“RustyKey”,“0”&gt;进入哈希查找集合,然后当出现RustyKey请求时,查找集合中的结果值。

当然,你不能像包含代码一样做很复杂的事情,但是你会遇到很多麻烦。

座右铭:“eval是邪恶的”。