RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1242192
Accepted
Mikhail Murugov
Mikhail Murugov
Asked:2022-02-10 14:14:21 +0000 UTC2022-02-10 14:14:21 +0000 UTC 2022-02-10 14:14:21 +0000 UTC

将类序列化为查询字符串

  • 772

有一项任务是在查询字符串(URL 字符串中的参数)中序列化自定义类。类的一些字段必须是强制性的,一些是可选的。例如:

public class UserQueryParameters {
    String name; // *
    int age; // Не обязательно
    String sex; // Не обязательно
}

您需要以某种方式声明要序列化的字段(在 Java 现实中,这称为编组,排序),哪些不是,然后将生成的结构转换为形式的字符串

name=John&age=23

如何实施?


尝试使用lombok注释:

import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class UserQueryParameters {
    String name; // *
    int age; // Не обязательно
    String sex; // Не обязательно
}

接下来,通过builder,形成一个填充字段的类实例:

UserQueryParameters params = UserQueryParameters.builder()
        .name("John")
        .sex("m")
        .build();

并通过反射形成查询字符串:

import java.lang.reflect.Field;

// ...

for (Field field : params.getClass().getDeclaredFields()) {
    if (field.get(params) != null) {
        addToQueryString(field.get(params));
    }
}

但是,显然,这样的解决方案是不合适的,因为。例如,对于一个int字段,不会是null,而是0。

java
  • 2 2 个回答
  • 10 Views

2 个回答

  • Voted
  1. Best Answer
    Pak Uula
    2022-02-11T11:44:32Z2022-02-11T11:44:32Z

    实际上,您的问题包含了一半的答案:)

    有必要以某种方式声明要序列化哪些字段(在 Java 现实中,这称为编组,有点),哪些不是

    为此,java 使用了注解机制。您需要创建自己的注释,用它们标记类中的字段,然后在编组器中通过反射遍历类对象的字段,从中选择带注释的字段,并key=value从标记的字段中为请求生成对.

    我做了一个简单的例子。QueryField使用两个参数创建注释

    • required定义该字段是否是必需的。默认情况下true- 这样做是为了明确指定可选字段。
    • name指定序列化时使用的名称。如果未设置,则使用字段名称。

    下面是一个如何使用注解的示例。具有字段age和的name类sex。只有字段是必需name的,字段sex被序列化为 gender.

        public static class TestData {
            @QueryField
            public String name; // *
            @QueryField(required = false)
            public Integer age; // Не обязательно
            @QueryField(name = "gender", required = false)
            public String sex; // Не обязательно
        }
    

    java中的注解由关键字定义@interface:

    package org.example.query;
    
    import java.lang.annotation.*;
    
    // Keep annotation in generated byte code
    @Retention(RetentionPolicy.RUNTIME)
    // Compiler hint: the annotation should be applied to fields only
    @Target(ElementType.FIELD)
    public @interface QueryField {
        // Custom field name in the generated query string
        public String name() default "";
        // If `true` the field value must be non-null 
        public boolean required() default true;
    }
    

    (Java 语言规范中的更多详细信息)

    现在我们可以继续编写序列化程序,但作为测试驱动开发的粉丝,我将从test开始。贴在这里太大了,所以我把它放在pastebin上。

    我将仅解释测试的想法。

    1. 准备数据。
    2. 生成字符串。
    3. 由于一般不定义字符串中字段的顺序,与其与参考字符串进行比较,不如解析字符串并将值添加到容器中。
    4. 然后,在测试中,检查在容器中找到了哪些值。
    5. 同时,检查那里没有多余的东西。
        // Тест для сериализатора с одним отсуствующим необязательным полем.
        @Test
        void test_serialize() throws SerializerException {
            TestData sample = new TestData();
            sample.age = 23;
            sample.name = "John";
            
            String actual = Serializer.buildQuery(sample);
            
            Map<String, String> data = parseQuery(actual);
            
            assertTrue(data.containsKey("age"));
            assertTrue(data.containsKey("name"));
            // Проверяем, что других полей нет.
            assertEquals(2, data.keySet().size());
            
            assertEquals("John", data.get("name"));
            assertEquals("23", data.get("age"));
            
        }
    

    现在测试已经准备好了,我们可以编写一个简单的序列化程序来说明这个想法。

    那里的一切都很简单。首先,我们遍历对象的字段并查找带有注释的字段QueryField。如果找到这样的字段,我们会尝试提取该字段。作为响应,JRE 可能会抛出异常——我们正在处理它们。

    然后我们同时检查该字段是否为空和可选。我们跳过这些字段。我们抛出一个关于空的必填字段的异常。

    然后我们将值转换为字符串(我们为每个消防员筛选所有危险字符)并选择一个名称 - 在注释中指定或从字段名称中获取。我们将“name=valueText”对添加到结果字符串中。利润!

        public static String buildQuery(Object obj) throws SerializerException {
            StringBuilder result = new StringBuilder();
            
            for (java.lang.reflect.Field fld : obj.getClass().getFields()) {
                QueryField annot = fld.getAnnotation(QueryField.class);
                if (annot == null) {
                    // This field has no "QueryField" annotation, ignore it.
                    continue;
                }
                
                Object fldVal;
                try {
                    fldVal = fld.get(obj);
                } catch (IllegalArgumentException e) {
                    throw new RuntimeException("Should never happen", e);
                } catch (IllegalAccessException e) {
                    throw new SerializerException("Failed to access field "+fld.getName(), e);
                }
                
                if (fldVal == null) {
                    if (annot.required()) {
                        throw new SerializerException("Required field is null");
                    } else {
                        // Ignore optional fields
                        continue;
                    }
                }
                
                String valText = urlEncode(fldVal.toString());
                
                String valName = annot.name();
                if (valName.length() == 0) {
                    // No custom name, use field name instead
                    valName = fld.getName();
                }
                valName = urlEncode(valName); 
                
                result.append('&').append(valName).append('=').append(valText);
            }
            return result.substring(1);
        }
    

    不知何故,这就是它的完成方式。

    • 1
  2. Aziz Umarov
    2022-02-10T15:30:27Z2022-02-10T15:30:27Z

    因为您正在处理字符串请求。我将首先序列化为带有字符串字段的中间类。为了不在转换中出现未处理的错误。

    public class ResuestUserQueryParameters {
        String name; 
        String age; 
        String sex; 
    }
    

    并且已经对其进行了进一步处理,检查了请求的值和其他值。

    而且由于通常请求本身会默认为您完成。(查询将被解析为行)。您基本上需要自己检查和处理参数

    • 0

相关问题

  • wpcap 找不到指定的模块

  • 如何以编程方式从桌面应用程序打开 HTML 页面?

  • Android Studio 中的 R.java 文件在哪里?

  • HashMap 初始化

  • 如何使用 lambda 表达式通过增加与原点的距离来对点进行排序?

  • 最大化窗口时如何调整元素大小?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    表格填充不起作用

    • 2 个回答
  • Marko Smith

    提示 50/50,有两个,其中一个是正确的

    • 1 个回答
  • Marko Smith

    在 PyQt5 中停止进程

    • 1 个回答
  • Marko Smith

    我的脚本不工作

    • 1 个回答
  • Marko Smith

    在文本文件中写入和读取列表

    • 2 个回答
  • Marko Smith

    如何像屏幕截图中那样并排排列这些块?

    • 1 个回答
  • Marko Smith

    确定文本文件中每一行的字符数

    • 2 个回答
  • Marko Smith

    将接口对象传递给 JAVA 构造函数

    • 1 个回答
  • Marko Smith

    正确更新数据库中的数据

    • 1 个回答
  • Marko Smith

    Python解析不是css

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5