如何在Python 3中将含逻辑运算符的查询字符串转为字典或JSON对象
Got it, let's tackle this problem. You want to turn a human-readable logical query string like ((blue AND green) OR (brown AND green) OR green) AND NOT red into that nested dictionary/JSON structure you provided. Here's a step-by-step solution with code that handles this parsing and conversion smoothly.
Core Approach
First, let's outline the key steps we'll take:
- Clean up the input string to standardize formatting and operator casing
- Recursively parse the expression (since logical queries are inherently nested)
- Prioritize handling nested parentheses first
- Split expressions by top-level operators (AND/OR/NOT) that aren't trapped inside parentheses
- Build the required dictionary structure as we parse each segment
Full Python Implementation
import re import json def preprocess_query(query): # Remove extra whitespace and standardize operators to lowercase cleaned = re.sub(r'\s+', ' ', query.strip()) cleaned = re.sub(r'\bAND\b', 'and', cleaned) cleaned = re.sub(r'\bOR\b', 'or', cleaned) cleaned = re.sub(r'\bNOT\b', 'not', cleaned) return cleaned def parse_query(query): query = preprocess_query(query) # Helper to find top-level operators (not enclosed in parentheses) def find_top_operator(s): paren_count = 0 for i, char in enumerate(s): if char == '(': paren_count += 1 elif char == ')': paren_count -= 1 elif paren_count == 0 and char == ' ': # Check if this space precedes a valid operator if s[i+1:i+4] in ['and', 'not'] or s[i+1:i+3] == 'or': operator_len = 4 if s[i+1:i+4] in ['and', 'not'] else 3 operator_end = i + operator_len # Ensure it's a standalone operator (not part of a word) if operator_end <= len(s) and s[operator_end:operator_end+1] in [' ', ')']: return (s[i+1:operator_end], i) return (None, -1) # Helper to strip redundant outer parentheses def strip_outer_parens(s): if s.startswith('(') and s.endswith(')'): paren_count = 0 for i, char in enumerate(s): if char == '(': paren_count += 1 elif char == ')': paren_count -= 1 if paren_count == 0 and i == len(s)-1: return s[1:-1] return s query = strip_outer_parens(query) # Handle NOT operator first (highest precedence after parentheses) if query.startswith('not '): term = query[4:].strip() return { "operator": "not", "filters": [parse_query(term)] } # Find and split on top-level AND/OR operator, split_idx = find_top_operator(query) if operator: left_segment = query[:split_idx].strip() right_segment = query[split_idx + len(operator) + 1:].strip() # Parse both segments recursively left_filter = parse_query(left_segment) right_filter = parse_query(right_segment) # Build the operator structure return { "operator": operator, "filters": [left_filter, right_filter] } else: # Single term, return base filter structure return { "means": "contains", "value": query.strip() } # Test with your example query query_str = "((blue AND green) OR (brown AND green) OR green) AND NOT red" result = {"filter": parse_query(query_str)} # Print formatted JSON output print(json.dumps(result, indent=2))
What This Code Does
Let's break down the key parts:
- Preprocessing: Cleans up messy whitespace and converts all operators to lowercase to avoid case sensitivity issues.
- Parentheses Handling: Removes unnecessary outer parentheses to simplify parsing nested expressions.
- Top-level Operator Detection: Scans the string to find operators that aren't inside parentheses—this ensures we parse the expression in the correct order of operations.
- Recursive Parsing:
- For
NOTclauses, wraps the parsed term in a "not" operator structure. - For
AND/ORclauses, splits the query, parses each side, and combines them into the required nested structure. - For single keywords, returns the base
{"means": "contains", "value": "..."}filter.
- For
Sample Output
Running the code with your example query will produce this structured JSON (matching your requested format):
{ "filter": { "operator": "and", "filters": [ { "operator": "or", "filters": [ { "operator": "and", "filters": [ { "means": "contains", "value": "blue" }, { "means": "contains", "value": "green" } ] }, { "operator": "and", "filters": [ { "means": "contains", "value": "brown" }, { "means": "contains", "value": "green" } ] }, { "means": "contains", "value": "green" } ] }, { "operator": "not", "filters": [ { "means": "contains", "value": "red" } ] } ] } }
You can extend this code easily—for example, adding support for different means values (like "equals" instead of "contains") by modifying the base case in the parse_query function.
内容的提问来源于stack exchange,提问作者Droid

