我如何创建一个通用的“示例” (实际上)任何给定类型的对象?

发布于 2024-12-29 07:30:36 字数 3621 浏览 2 评论 0原文

我正在开发一个元数据生成器,它基本上可以自动生成 REST API 的文档。

其中一部分包括显示请求/响应类型,当然可以是 DTO。我想要的是对象的序列化 JSON(或 XML)版本,显示结构和占位符数据。 (序列化部分很简单,从创建对象开始就很困难)。例如,给定对象:

public class MyObject {
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Active { get; set; }
}

我希望能够调用某个函数:

var obj = GetDefaultValue(typeof(MyObject));

并得到相当于:

new MyObject { Name = string.Empty, Age = 0, Active = false };
// or in other words:  new MyObject { Name = default(string), Age = default(int), Active = default(bool) };

我有一些基本代码开始执行此操作:

    /// <summary>
    /// Class to create a default value for a specified type.
    /// </summary>
    public abstract class DefaultValueGenerator
    {
        private DefaultValueGenerator() { }

        /// <summary>
        /// Creates a new default instance of type T.
        /// Requires that T has a parameter-less constructor, or can be created using <code>default(T)</code>
        /// </summary>
        /// <param name="T"></param>
        /// <returns></returns>
        public static object GetDefaultValue(Type T)
        {
            try
            {
                return System.Activator.CreateInstance(T, true);
                //TODO: if array type, also create a single item of that type
            }
            catch (Exception activatorException)
            {
                try
                {
                    // from http://stackoverflow.com/a/2490267/7913
                    var defaultGeneratorType = typeof(DefaultGenerator<>).MakeGenericType(T);

                    return defaultGeneratorType.InvokeMember(
                      "GetDefault",
                      BindingFlags.Static |
                      BindingFlags.Public |
                      BindingFlags.InvokeMethod,
                      null, null, new object[0]);
                }
                catch //(Exception defaultGeneratorException)
                {
                    throw new MissingMethodException(string.Format("No parameterless constructor defined for model {0}", T.Name), activatorException);
                }

            }

        }

        // from http://stackoverflow.com/a/2490267/7913
        private class DefaultGenerator<T>
        {
            public static T GetDefault()
            {
                return default(T);
            }
        }


    }

因此,使用它您可以调用:

var obj = DefaultValueGenerator.GetDefaultValue(typeof(MyObject));

此实现的一个问题是,如果您调用 DefaultValueGenerator.GetDefaultValue(typeof(string)) 它会抛出我捕获为 activatorException 的异常,然后使用 default 关键字。只是丑陋,因为我依赖于异常..有更好的方法吗?


第二个问题是数组/集合。例如: DefaultValueGenerator.GetDefaultValue(typeof(List)) 创建一个 0 元素列表,该列表又序列化为 JSON 作为 [] ——不是很有帮助在文档方面。我希望它能生成一个元素。


第三个问题是嵌套类型。例如,如果我有:

public class MyContainerObject {
    public MyObject OtherObject { get; set; }
    public int SomeValue { get; set; }
}

我希望它生成等价的:

var obj = new MyContainerObject { 
    OtherObject = new MyObject { Name = string.Empty, Age = 0, Active = false },
    SomeValue = 0,
}

但实际上,它生成 OtherObject 作为空值。


有人知道一些代码/库已经做到了这一点吗?否则,关于如何实现这一目标并避免我指出的一些陷阱有什么建议吗?有没有其他更简单的方法来解决这个问题?

我希望它适用于内置基本类型(字符串、整数、guid 等)以及任何更复杂的对象——只要它们有一个无参数构造函数(我对此限制很满意,因为无论如何,使用的类型应该是 POCO/DTO)。

I'm working on a metadata generator that basically auto-generates documentation for a REST API.

Part of this includes showing the request/response types, which of course can be DTOs. What I'd like is a serialized JSON (or XML) version of the object, showing the structure and placeholder data. (The serialization part is easy, it's creating the object to begin with that's hard). So for example, given the object:

public class MyObject {
    public string Name { get; set; }
    public int Age { get; set; }
    public bool Active { get; set; }
}

I want to be able to call some function:

var obj = GetDefaultValue(typeof(MyObject));

and get the equivalent of:

new MyObject { Name = string.Empty, Age = 0, Active = false };
// or in other words:  new MyObject { Name = default(string), Age = default(int), Active = default(bool) };

I have some basic code that starts to do this:

    /// <summary>
    /// Class to create a default value for a specified type.
    /// </summary>
    public abstract class DefaultValueGenerator
    {
        private DefaultValueGenerator() { }

        /// <summary>
        /// Creates a new default instance of type T.
        /// Requires that T has a parameter-less constructor, or can be created using <code>default(T)</code>
        /// </summary>
        /// <param name="T"></param>
        /// <returns></returns>
        public static object GetDefaultValue(Type T)
        {
            try
            {
                return System.Activator.CreateInstance(T, true);
                //TODO: if array type, also create a single item of that type
            }
            catch (Exception activatorException)
            {
                try
                {
                    // from http://stackoverflow.com/a/2490267/7913
                    var defaultGeneratorType = typeof(DefaultGenerator<>).MakeGenericType(T);

                    return defaultGeneratorType.InvokeMember(
                      "GetDefault",
                      BindingFlags.Static |
                      BindingFlags.Public |
                      BindingFlags.InvokeMethod,
                      null, null, new object[0]);
                }
                catch //(Exception defaultGeneratorException)
                {
                    throw new MissingMethodException(string.Format("No parameterless constructor defined for model {0}", T.Name), activatorException);
                }

            }

        }

        // from http://stackoverflow.com/a/2490267/7913
        private class DefaultGenerator<T>
        {
            public static T GetDefault()
            {
                return default(T);
            }
        }


    }

So, using this you can call:

var obj = DefaultValueGenerator.GetDefaultValue(typeof(MyObject));

One problem this implementation is that if you call DefaultValueGenerator.GetDefaultValue(typeof(string)) it throws the exception I catch as activatorException and then uses the default keyword. Just ugly because I'm relying on an exception.. is there a better way?


Second issue is arrays/collections. For example: DefaultValueGenerator.GetDefaultValue(typeof(List<MyObject>)) creates a 0-element list, which in turn serializes to JSON as [] -- not very helpful in terms of documentation. I'd like this to generate one element.


Third issue is nested types. For example, if I have:

public class MyContainerObject {
    public MyObject OtherObject { get; set; }
    public int SomeValue { get; set; }
}

I'd like this to generate the equivalent of:

var obj = new MyContainerObject { 
    OtherObject = new MyObject { Name = string.Empty, Age = 0, Active = false },
    SomeValue = 0,
}

but in fact, it generates OtherObject as a null value.


Anyone know of some code/library that does this already? Otherwise, any tips on how to accomplish this, and avoid some pitfalls I've pointed out? Is there a different way to solve this that would be easier?

I'd like this to work for built-in basic types (string, int, guid, etc) as well as any more complex objects -- so long as they have a parameter-less constructor (I am fine with that limitation, since the types used should be POCO's/DTO's anyway).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

予囚 2025-01-05 07:30:36

完成重写

我看到你有DefaultGenerator的源代码。您可以创建 TryMakeGenericType 重载来消除丑陋的 try...catch 块。这应该会使代码更加优雅。它不会消除抛出异常的事实,但会隐藏它。

关于列表,有没有办法确定列表中存储的项目的类型?我认为一旦有了这个,渲染代码以插入代码来初始化该类型的成员应该是一件相对简单的事情。

嵌套类型似乎与您在列表中遇到的问题相同。事实上,我敢打赌,一旦你解决了一个问题,另一个问题的解决方案就会变得显而易见。然而,如果没有看到所有的来源,就很难判断。

我同意,如果有一个图书馆已经做到了这一点,那就太好了。我很乐意在这里为我们的 XML API 找到类似的东西。短期内,您可能必须扩展现有的资源,直到找到合适的替代方案。

COMPLETE Rewrite

I see that you have the source code for the DefaultGenerator. You could create a TryMakeGenericType overload to eliminate the ugly try...catch block. That should make the code more elegant. It won't eliminate the fact that an exception is being thrown, but it will hide it.

Regarding lists, is there no way to determine the type of the items stored in the list? I would think that once you have this, it should be a relatively straightforward matter to render the code to insert the code to initialize member of that type.

Nested types appear to be the same problem you're running into with lists. In point of fact, I'd wager that once you solve the one problem, the solution to the other would become readily apparent. Without seeing all of the source, though, it's hard to tell.

I agree, it would be very nice to have a library that already does this. I would love to find something like it for our XML API here. You're likely going to have to extend what you have, in the short term, until a suitable alternative can be found.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文