我正在重新发布这个问题,因为有人将其标记为重复但不是,所以我详细说明了以避免混淆。
我正在使用c#创建一个API,我的WebApp可以通过它来获取一些数据。在我的网络应用程序中,我有一个包含(假设)产品列表的视图。现在我有一条路线可以返回存储在数据库中的所有产品。
我现在想添加webapp的功能来制作复杂的过滤器。我希望能够在webApp或者Postman中使用一个名为“过滤器”的URlParam到达我的路线(比方说)“/ api / product”,它将包含webapp需要应用的所有过滤器。
现在回到我的WebApp(使用angular构建)我有一个可以像过滤器树一样创建的服务(参见下面的类体系结构)。 (是的,这种架构可以改进)
我的想法是我可以构建这样的过滤器:如果我想获取所有名称中包含“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的实例。
可以这样做吗?
答案 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());
}
}
}
}