使用可变内容

时间:2017-11-20 14:52:30

标签: c# .net json deserialization

我正在重新发布这个问题,因为有人将其标记为重复但不是,所以我详细说明了以避免混淆。

我正在使用c#创建一个API,我的WebApp可以通过它来获取一些数据。在我的网络应用程序中,我有一个包含(假设)产品列表的视图。现在我有一条路线可以返回存储在数据库中的所有产品。

我现在想添加webapp的功能来制作复杂的过滤器。我希望能够在webApp或者Postman中使用一个名为“过滤器”的URlParam到达我的路线(比方说)“/ api / product”,它将包含webapp需要应用的所有过滤器。

现在回到我的WebApp(使用angular构建)我有一个可以像过滤器树一样创建的服务(参见下面的类体系结构)。 (是的,这种架构可以改进)

enter image description here

我的想法是我可以构建这样的过滤器:如果我想获取所有名称中包含“foo”并且价格低于15美元的产品:

let filter = new AndNode();
filter.Add(new LikeNode("name", "foo"));
filter.Add(new GTNode("price", 15));
filter.generateJson();

将导致这个Json:

{
    "$and": [
        { 
            "$like": { "key": "name", "value": "foo" }
        },
        {
            "$gt": { "key": "price", "value": 15 }
        }
    ]
}  

现在,这个Json有一个要求:它应该永远不会在第一级包含多个属性。因为这是用于生成SQL过滤器,所以json必须在其第一级只包含一个节点:EQ,LIKE,GT或LT节点,或者如果我们有多个过滤器AND / OR节点定义下一级别的每个过滤器之间的逻辑“连接”。

所以有道理:

{
    "$like": { "key": "name", "value": "foo" }
}

有效并可能导致此SQL语句:

SELECT * FROM products WHERE name LIKE "%foo%";

另外:

{
    "$and": [
        { 
            "$like": { "key": "name", "value": "foo" }
        },
        {
            "$gt": { "key": "price", "value": 15 }
        }
    ]
}  

有效并且可能导致此sql语句:

SELECT * FROM products WHERE name LIKE "%foo%" AND price > 15;

但是:

{
    "$like": { "key": "name", "value": "foo" },
    "$eq": { "key": "price", "value": 5 },
}

无效,因为它会产生这个sql语句:

SELECT * FROM products WHERE name LIKE "%foo%" price > 15

由于缺少AND或OR关键字,女巫无效。

我想做什么: 在一个美丽的世界中,我希望能够在我的Web应用程序中序列化这个json,将其转换为有效的URL参数字符串,并将其发送到API(c#)反序列化该JSON以获得与我相同的对象结构在WebApp上。 对于我们带有价格和名称的示例,我希望有一个AndNode类的实例,其成员“child”包含2个对象:1个LikeNode类实例和1个GTNode类实例。

这个实现可能有争议,但这正是我们现在所需要的。

现在,我有:

public class EQNode
{
    [JsonProperty("key")]
    public string key { get; set; }

    [JsonProperty("value")]
    public int value { get; set; }
}

public class LikeNode
{
    [JsonProperty("key")]
    public string key { get; set; }

    [JsonProperty("value")]
    public string value { get; set; }
}

public class GTNode
{
    [JsonProperty("key")]
    public string key { get; set; }

    [JsonProperty("value")]
    public int value { get; set; }
}

public class LTNode
{
    [JsonProperty("key")]
    public string key { get; set; }

    [JsonProperty("value")]
    public int value { get; set; }
}

public class OrNode
{
    [JsonProperty("$eq")]
    public EQNode eq { get; set; }
}

public class AndNode
{
    [JsonProperty("$eq")]
    public EQNode eq { get; set; }

    [JsonProperty("$like")]
    public LikeNode like { get; set; }

    [JsonProperty("$gt")]
    public GTNode gt { get; set; }

    [JsonProperty("$lt")]
    public LTNode lt { get; set; }

    [JsonProperty("$or")]
    public List<OrNode> or { get; set; }

    [JsonProperty("$and")]
    public List<OrNode> and { get; set; }
}

public class RootObject
{
    [JsonProperty("$and")]
    public List<AndNode> and { get; set; }

    [JsonProperty("$or")]
    public List<OrNode> or { get; set; }

    [JsonProperty("$eq")]
    public EQNode eq { get; set; }

    [JsonProperty("$like")]
    public LikeNode like { get; set; }

    [JsonProperty("$gt")]
    public GTNode gt { get; set; }

    [JsonProperty("$lt")]
    public LTNode lt { get; set; }
}

这是由visual studio的“过去特殊”功能生成的(我添加了所有JsonProperty装饰器以匹配我的json中的名称)。 它有效,但是,这并不是我所期望的。

首先,我想从我的WebApp结构(具有继承)获得最接近的结构。 其次,我不喜欢这个生成的代码处理我可以在我的根对象中拥有$ eq,$ gt,$ lt,$ like,$或or和node的事实,我无法相信没办法像这样干净的结构: (注意:这是出于示例目的,使用的一些装饰器不存在或者使用不当,这仅用于演示)

public class RootObject {
    public FilterNode root;
} 

public class FilterNode {
}

public class ConjunctionNode: FilterNode {
    public FilterNode[] childs
}

[JsonObject("$and")]
public class AndNode: ConjunctionNode {
}

[JsonObject("$or")]
public class OrNode: ConjunctionNode {
}

[JsonObject("$like")]
public class LikeNode: FilterNode {
    public string key;
    public string value;
}

[JsonObject("$eq")]
public class EQNode: FilterNode {
    public string key;
    public string value;
}

[JsonObject("$gt")]
public class GTNode: FilterNode {
    public string key;
    public string value;
}

[JsonObject("$lt")]
public class LTNode: FilterNode {
    public string key;
    public string value;
}

所以,如果我们使用样本json:

{
    "$and": [
        { 
            "$like": { "key": "name", "value": "foo" }
        },
        {
            "$gt": { "key": "price", "value": 15 }
        }
    ]
}  

我可以使用:

RootObject obj = JsonConvert.DeserializeObject<RootObject>(filters);

将RootObject的“root”成员作为AndNode的一个实例。 这个AndNode在这个子节点中包含两个对象,它们是LikeNode的一个实例和一个GTNode的实例。

可以这样做吗?

1 个答案:

答案 0 :(得分:1)

我详细解决了你的问题。您倾向于反序列化的对象模型存在缺陷。此外,您无法使用直接反序列化,因为密钥可能会不断变化。因此,您必须逐步完成json并检测并将其存储在字典或您选择的任何数据类型中。

以下是建议的对象类:

using System.Collections.Generic;

namespace ConsoleApp1
{
    public class RootObject
    {
        public RootObject()
        {
            ConjunctionNode = new List<ConjunctionNode>();
        }

        public List<ConjunctionNode> ConjunctionNode { get; set; }
    }

    public class FilterNode
    {
        public string Key { get; set; }
        public string Value { get; set; }
    }

    public class ConjunctionNode
    {
        public LikeNode Like { get; set; }
        public EQNode Eq { get; set; }
        public GTNode Gt { get; set; }
        public LTNode Lt { get; set; }
    }

    public class AndNode : ConjunctionNode
    {
    }

    public class OrNode : ConjunctionNode
    {
    }

    public class LikeNode : FilterNode
    {
    }

    public class EQNode : FilterNode
    {
    }

    public class GTNode : FilterNode
    {
    }

    public class LTNode : FilterNode
    {
    }
}

查看对象是如何制作的。现在,您需要逐步阅读json并创建分叉并存储然后阅读它们,或者阅读它们并一起创建查询。

这里我刚刚开始获取数据,你可以尝试一次使用这种方法。如果您遇到困难,请随时回来,我可以做更多的工作或找出其他解决方法。

using Newtonsoft.Json;
using System.IO;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            RootObject rootObject = new RootObject();
            string json = @"{ ""$and"": [ { ""$like"": { ""key"": ""name"", ""value"": ""foo"" } }, {""$gt"": { ""key"": ""price"", ""value"": 15 } } ] }  ";
            //var rootObject = JsonConvert.DeserializeObject<RootObject>(json);


            using (var reader = new JsonTextReader(new StringReader(json)))
            {
                while (reader.Read())
                {
                    //Console.WriteLine("{0} - {1} - {2}", reader.TokenType, reader.ValueType, reader.Value);
                    if (reader.TokenType.ToString() == "PropertyName")
                    {
                        //Console.WriteLine("Hi");
                        CreateConjunctionNode(reader, rootObject);
                        //CreateFilterNode(reader, rootObject);
                        //Console.WriteLine(reader.Value);
                    }
                }
            }
        }

        private static void CreateFilterNode(JsonTextReader reader, RootObject rootObject)
        {
            if (reader.Value.ToString() == "$like")
            {
                LikeNode likeNode = new LikeNode();
            }
            else if (reader.Value.ToString() == "$gt")
            {
                GTNode gTNode = new GTNode();
            }
            else if (reader.Value.ToString() == "$lt")
            {
                LTNode lTNode = new LTNode();
            }
            else if (reader.Value.ToString() == "$eq")
            {
                EQNode eQNode = new EQNode();
            }
        }

        private static void CreateConjunctionNode(JsonTextReader reader, RootObject rootObject)
        {
            if (reader.Value.ToString() == "$and")
            {
                rootObject.ConjunctionNode.Add(new AndNode());
            }
            else if (reader.Value.ToString() == "$or")
            {
                rootObject.ConjunctionNode.Add(new OrNode());
            }
        }
    }
}