最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

多層嵌套的json的值如何解析/替換

 更新時(shí)間:2023年10月31日 10:49:24   作者:linyb極客之路  
這篇文章主要介紹了多層嵌套的json的值如何解析/替換的方法示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

前言

前陣子承接了2個(gè)需求,一個(gè)數(shù)據(jù)脫敏,一個(gè)是低代碼國際化多語言需求,這兩個(gè)需求有個(gè)共同特點(diǎn),都是以json形式返回給前端,而且都存在多層嵌套,其中數(shù)據(jù)脫敏的數(shù)據(jù)格式是比較固定,而低代碼json的格式存在結(jié)構(gòu)固定和不固定 2種格式。最后不管是數(shù)據(jù)脫敏或者是多語言,業(yè)務(wù)抽象后,都存在需要做json值替換的需求。今天就來聊下多層嵌套json值如何解析或者替換

多層嵌套json解析

1、方法一:循環(huán)遍歷+利用正則進(jìn)行解析

這種做法相對常規(guī),且解析比較繁瑣。

2、方法二:利用OGNL表達(dá)式

1、何為OGNL

OGNL(Object-Graph Navigation Language)是一種表達(dá)式語言,用于在Java應(yīng)用程序中對對象圖進(jìn)行導(dǎo)航和操作。OGNL本身并不提供直接的執(zhí)行環(huán)境,它是作為一個(gè)庫或框架的一部分來使用的。因此,OGNL的執(zhí)行方式取決于使用它的上下文。

一般情況下,OGNL可以通過兩種方式執(zhí)行:解釋執(zhí)行和編譯執(zhí)行。

解釋執(zhí)行:在解釋執(zhí)行中,OGNL表達(dá)式在運(yùn)行時(shí)逐條解釋和執(zhí)行。它會在每次表達(dá)式執(zhí)行時(shí)動態(tài)計(jì)算表達(dá)式的結(jié)果,并根據(jù)對象圖的實(shí)際狀態(tài)進(jìn)行導(dǎo)航和操作。這種方式的靈活性較高,可以根據(jù)需要對對象圖進(jìn)行動態(tài)操作,但相對而言執(zhí)行效率較低。

編譯執(zhí)行:為了提高執(zhí)行效率,有些框架會將OGNL表達(dá)式編譯成可執(zhí)行的字節(jié)碼或類文件。在編譯執(zhí)行中,OGNL表達(dá)式在編譯階段被轉(zhuǎn)換成可執(zhí)行代碼,然后在運(yùn)行時(shí)直接執(zhí)行這些生成的代碼。這種方式可以在一定程度上提高執(zhí)行速度,但犧牲了一些靈活性,因?yàn)榫幾g后的代碼在運(yùn)行時(shí)不再動態(tài)計(jì)算。

我們經(jīng)常使用ORM框架mybatis的動態(tài)sql解析,它的實(shí)現(xiàn)基石就是OGNL表達(dá)式?;氐秸},我們?nèi)绾卫肙GNL來解析json

a、 在項(xiàng)目POM引入OGNL GAV

<dependency>
            <groupId>ognl</groupId>
            <artifactId>ognl</artifactId>
            <version>${ognl.version}</version>
        </dependency>

b、 封裝OGNL表達(dá)式工具類

public final class OgnlCache {

  private static final OgnlMemberAccess MEMBER_ACCESS = new OgnlMemberAccess();
  private static final OgnlClassResolver CLASS_RESOLVER = new OgnlClassResolver();
  private static final Map<String, Object> expressionCache = new ConcurrentHashMap<>();

  private OgnlCache() {
    // Prevent Instantiation of Static Class
  }

  public static Object getValue(String expression, Object root) {
    try {
      Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null);
      return Ognl.getValue(parseExpression(expression), context, root);
    } catch (OgnlException e) {
      throw new RuntimeException("Error evaluating expression '" + expression + "'. Cause: " + e, e);
    }
  }

  private static Object parseExpression(String expression) throws OgnlException {
    Object node = expressionCache.get(expression);
    if (node == null) {
      node = Ognl.parseExpression(expression);
      expressionCache.put(expression, node);
    }
    return node;
  }

}

c、 封裝json工具類

public final class JsonUtil {

    private JsonUtil(){}

    public static <T> T parse(String jsonStr, Class<T> clazz) throws Exception {
        return JSON.parseObject(jsonStr, clazz);
    }

    public static Object getValue(Map map, String path) throws Exception {
         return OgnlCache.getValue(path,map);
    }
}

d、 多層嵌套json解析例子

private void printMenuI18nCodeByOgnl() throws Exception {
        String menuJson = mockMenuService.getMenuJson();
        Map<String, Object> map = JsonUtil.parse(menuJson, Map.class);
        Object topMenu = JsonUtil.getValue( map,"i18NCode");
        Object userMenu = JsonUtil.getValue( map,"children[0].i18NCode");
        Object userMenuAdd = JsonUtil.getValue( map,"children[0].children[0].i18NCode");
        Object userMenuUpdate = JsonUtil.getValue( map,"children[0].children[1].i18NCode");
        Object deptMenu = JsonUtil.getValue( map,"children[1].i18NCode");
        Object deptMenuList = JsonUtil.getValue( map,"children[1].children[0].i18NCode");
        Object deptMenuDelete = JsonUtil.getValue( map,"children[1].children[1].i18NCode");
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl Start <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,");
        System.out.println(topMenu);
        System.out.println(userMenu);
        System.out.println(userMenuAdd);
        System.out.println(userMenuUpdate);
        System.out.println(deptMenu);
        System.out.println(deptMenuList);
        System.out.println(deptMenuDelete);
        System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl End <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,");
    }

注: 示例中的menuJson形如下

{"children":[{"children":[{"children":[],"component":"saas/index","i18NCode":"user.menu.add","id":8,"linkUrl":"/user/add","menuName":"用戶新增","parentId":9,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"user.menu.update","id":7,"linkUrl":"/user/update","menuName":"用戶編輯","parentId":9,"sort":9999}],"component":"saas/index","i18NCode":"user.menu","id":9,"linkUrl":"/user","menuName":"用戶菜單","parentId":1,"sort":9999},{"children":[{"children":[],"component":"saas/index","i18NCode":"dept.menu.list","id":11,"linkUrl":"/dept/list","menuName":"部門列表","parentId":10,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"dept.menu.delete","id":12,"linkUrl":"/dept/delete","menuName":"部門刪除","parentId":10,"sort":9999}],"component":"saas/index","i18NCode":"dept.menu","id":10,"linkUrl":"/dept","menuName":"部門菜單","parentId":1,"sort":9999}],"component":"saas/index","i18NCode":"top.menu","id":1,"linkUrl":"/topUrl","menuName":"頂級菜單","parentId":0,"sort":9999}

解析后控制臺打印如下

>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl Start <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,
top.menu
user.menu
user.menu.add
user.menu.update
dept.menu
dept.menu.list
dept.menu.delete
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> Print MenuI18nCode By Ognl End <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<,

OGNL表達(dá)式常用例子

1. 基本對象樹的訪問

對象樹的訪問就是通過使用點(diǎn)號將對象的引用串聯(lián)起來進(jìn)行。

例如:name,department.name,user.department.factory.manager.name

2. 對容器變量的訪問

對容器變量的訪問,通過#符號加上表達(dá)式進(jìn)行。

例如:#name,#department.name,#user.department.factory.manager.name

3. 使用操作符號

OGNL表達(dá)式中能使用的操作符基本跟Java里的操作符一樣,除了能使用 +, -, *, /, ++, --, ==, !=, = 等操作符之外,還能使用 mod, in, not in等。

4. 容器、數(shù)組、對象

OGNL支持對數(shù)組和ArrayList等容器的順序訪問:

例如:group.users[0]

同時(shí),OGNL支持對Map的按鍵值查找:

例如:#session['mySessionPropKey']

不僅如此,OGNL還支持容器的構(gòu)造的表達(dá)式:

例如:{"green", "red", "blue"}構(gòu)造一個(gè)List,#{"key1" : "value1", "key2" : "value2", "key3" : "value3"}構(gòu)造一個(gè)Map

你也可以通過任意類對象的構(gòu)造函數(shù)進(jìn)行對象新建:

例如:new java.net.URL("http://localhost/")

5. 對靜態(tài)方法或變量的訪問

要引用類的靜態(tài)方法和字段,他們的表達(dá)方式是一樣的@class@member或者@class@method(args):

例如:@com.javaeye.core.Resource@ENABLE,@com.javaeye.core.Resource@getAllResources

6. 方法調(diào)用

直接通過類似Java的方法調(diào)用方式進(jìn)行,你甚至可以傳遞參數(shù):

例如:user.getName(),group.users.size(),group.containsUser(#requestUser)

7. 投影和選擇

OGNL支持類似數(shù)據(jù)庫中的投影(projection) 和選擇(selection)。

投影就是選出集合中每個(gè)元素的相同屬性組成新的集合,類似于關(guān)系數(shù)據(jù)庫的字段操作。投影操作語法為 collection.{XXX},其中XXX 是這個(gè)集合中每個(gè)元素的公共屬性。

例如:group.userList.{username}將獲得某個(gè)group中的所有user的name的列表。

選擇就是過濾滿足selection 條件的集合元素,類似于關(guān)系數(shù)據(jù)庫的紀(jì)錄操作。選擇操作的語法為:collection.{X YYY},其中X 是一個(gè)選擇操作符,后面則是選擇用的邏輯表達(dá)式。而選擇操作符有三種:

? 選擇滿足條件的所有元素
^ 選擇滿足條件的第一個(gè)元素
$ 選擇滿足條件的最后一個(gè)元素

例如:group.userList.{? #this.name != null}將獲得某個(gè)group中user的name不為空的user的列表。

多層嵌套json替換

1、方法一:循環(huán)遍歷+正則進(jìn)行替換

這種做法相對常規(guī),且替換比較繁瑣。

2、方法二:利用json類庫,進(jìn)行替換

以fastJSON為例

a、 在項(xiàng)目pom引入fastJSON GAV

<dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>

b、 多層嵌套json替換例子

以將菜單的i18nCode替換為具體語言的值為例

public String reBuildMenuJson(){
        String orginalMenuJson = getMenuJson();
        JSONObject jsonObject = JSON.parseObject(orginalMenuJson);
        jsonObject.put(I18N_CODE_COLUMN,mockI18nCache.get(jsonObject.get(I18N_CODE_COLUMN)));
        reBuildChildJson(jsonObject);
        return JSON.toJSONString(jsonObject);

    }

    private void reBuildChildJson(JSONObject curentObject){
        JSONArray children = curentObject.getJSONArray(CHILDREN_COLUMN);
        for (int i = 0; i < children.size(); i++) {
            JSONObject child = children.getJSONObject(i);
            child.put(I18N_CODE_COLUMN,mockI18nCache.get(child.get(I18N_CODE_COLUMN)));
            reBuildChildJson(child);
        }

    }

注: 未替換前,menuJson形如下

{"children":[{"children":[{"children":[],"component":"saas/index","i18NCode":"user.menu.add","id":8,"linkUrl":"/user/add","menuName":"用戶新增","parentId":9,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"user.menu.update","id":7,"linkUrl":"/user/update","menuName":"用戶編輯","parentId":9,"sort":9999}],"component":"saas/index","i18NCode":"user.menu","id":9,"linkUrl":"/user","menuName":"用戶菜單","parentId":1,"sort":9999},{"children":[{"children":[],"component":"saas/index","i18NCode":"dept.menu.list","id":11,"linkUrl":"/dept/list","menuName":"部門列表","parentId":10,"sort":9999},{"children":[],"component":"saas/index","i18NCode":"dept.menu.delete","id":12,"linkUrl":"/dept/delete","menuName":"部門刪除","parentId":10,"sort":9999}],"component":"saas/index","i18NCode":"dept.menu","id":10,"linkUrl":"/dept","menuName":"部門菜單","parentId":1,"sort":9999}],"component":"saas/index","i18NCode":"top.menu","id":1,"linkUrl":"/topUrl","menuName":"頂級菜單","parentId":0,"sort":9999}

替換后,menuJson形如下

{"component":"saas/index","children":[{"component":"saas/index","children":[{"component":"saas/index","children":[],"linkUrl":"/user/add","menuName":"用戶新增","id":8,"sort":9999,"i18NCode":"userMenuAdd","parentId":9},{"component":"saas/index","children":[],"linkUrl":"/user/update","menuName":"用戶編輯","id":7,"sort":9999,"i18NCode":"userUpdateAdd","parentId":9}],"linkUrl":"/user","menuName":"用戶菜單","id":9,"sort":9999,"i18NCode":"userMenu","parentId":1},{"component":"saas/index","children":[{"component":"saas/index","children":[],"linkUrl":"/dept/list","menuName":"部門列表","id":11,"sort":9999,"i18NCode":"deptMenuList","parentId":10},{"component":"saas/index","children":[],"linkUrl":"/dept/delete","menuName":"部門刪除","id":12,"sort":9999,"i18NCode":"deptMenuDelete","parentId":10}],"linkUrl":"/dept","menuName":"部門菜單","id":10,"sort":9999,"i18NCode":"deptMenu","parentId":1}],"linkUrl":"/topUrl","menuName":"頂級菜單","id":1,"sort":9999,"i18NCode":"topMenu","parentId":0}

3、方法三:利用json序列化注解

以菜單國際化為示例

1、自定義注解

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@JacksonAnnotationsInside
@JsonSerialize(using = I18nJsonSerializer.class)
public @interface I18nField {
}

2、自定義國際化翻譯接口(該具體實(shí)現(xiàn)留給業(yè)務(wù)擴(kuò)展)

public interface I18nService {

    String getTargetContent(String i18nCode);
}

題外話 : 為啥不像spring的messageSource定義成

String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

因?yàn)楹芏鄥?shù)信息可以直接通過上下文獲取,比如Locale可以通過LocaleContextHolder.getLocale()

3、編寫json序列化接口

public class I18nJsonSerializer extends JsonSerializer<String> implements ContextualSerializer {
    @Autowired
    private I18nService i18nService;
    @Override
    public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
        jsonGenerator.writeString(i18nService.getTargetContent(s));
    }
    @Override
    public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
        I18nField i18nField = beanProperty.getAnnotation(I18nField.class);
        if(!ObjectUtils.isEmpty(i18nField) && String.class.isAssignableFrom(beanProperty.getType().getRawClass())){
            return this;
        }
        return serializerProvider.findValueSerializer(beanProperty.getType(),beanProperty);
    }
}

4、定義和json字段能夠匹配的對象

大白話,就是json和這個(gè)對象可以相互轉(zhuǎn)換。以菜單為例

@Data
@EqualsAndHashCode(callSuper = true, of = {"id"})
public class MenuResourceDTO extends TreeDTO<MenuResourceDTO> implements Serializable {
    private static final long serialVersionUID = 1L;
 。。。。。省略其他屬性
    /**
     * 單菜名稱
     */
    private String menuName;
    private String permission;
    /**
     * 是否緩存
     */
    private Integer keepAlive;
    @I18nField
    private String i18NCode;
    public static String I18N_CODE_COLUMN = "i18NCode";
    public static String CHILDREN_COLUMN = "children";

5、在需要進(jìn)行替換的字段上加上 @I18nField注解

@I18nField
    private String i18NCode;

6、替換驗(yàn)證

編寫一個(gè)測試controller,用來輸出替換后的菜單信息

@RestController
@RequestMapping("menu")
@RequiredArgsConstructor
public class MockMenuController {
    private final MockMenuService mockMenuService;
    @GetMapping
    public MenuResourceDTO getMenu(){
        return mockMenuService.getMenuResourceDTO();
    }
}

通過POSTMAN訪問,得到如下信息

{
    "id": 1,
    "parentId": 0,
    "sort": 9999,
    "children": [
        {
            "id": 9,
            "parentId": 1,
            "sort": 9999,
            "children": [
                {
                    。。。省略其他信息
                    "menuName": "用戶新增",
                    "i18NCode": "userMenuAdd"
                },
                {
                    。。。省略其他信息
                    "menuName": "用戶編輯",
                    "i18NCode": "userUpdateAdd"
                }
            ],
            。。。省略其他信息
            "menuName": "用戶菜單",
            "i18NCode": "userMenu"
        },
    "menuName": "頂級菜單",
    "i18NCode": "topMenu"
}

回答上面多層json解析的方法三,那個(gè)懸念做法就是將json與對象映射起來,通過對象來取值

總結(jié)

本文的多層嵌套json的解析和替換都提供了幾種方案,綜合來講是推薦將json先轉(zhuǎn)對象,通過對象操作。對json替換,推薦使用自定義json序列化注解的方式。但這種方式比較適合json的結(jié)構(gòu)以及字段是固定的方式。對于低代碼,本身的json結(jié)構(gòu)是多種多樣的,如果要后端實(shí)現(xiàn),一種做法,就是將這些json都映射成對象,但因?yàn)閖son結(jié)構(gòu)多種多樣,就會導(dǎo)致要映射的對象膨脹。另一種方式,是直接轉(zhuǎn)JsonObject,通過JsonObject來操作替換

其次現(xiàn)在都是前后端分離,有些東西其實(shí)也可以放在前端實(shí)現(xiàn),比如這種替換工作其實(shí)挺適合放在前端做的。以低代碼為例,因?yàn)榍岸吮緛砭托枰馕鰆son,后端可以維護(hù)一個(gè)映射表,前端實(shí)現(xiàn)一個(gè)組件函數(shù),通過該函數(shù)優(yōu)先從前端緩存取,取不到再從調(diào)用后端接口,這就是json替換的方法四,把替換工作留給前端做,哈哈。大家是一個(gè)團(tuán)隊(duì),哪邊好實(shí)現(xiàn),就放哪邊做

最后那個(gè)ognl的代碼,我是直接把mybatis的源碼搬過來,直接套用了。開源有的東西,就沒必要自己再搞一遍了

demo鏈接

以上就是多層嵌套的json的值如何解析/替換的詳細(xì)內(nèi)容,更多關(guān)于多層嵌套json值解析替換的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • Java中Redisson 的原理深度解析

    Java中Redisson 的原理深度解析

    Redisson是一個(gè)高性能的Redis客戶端,它通過將Redis數(shù)據(jù)結(jié)構(gòu)映射為Java對象和分布式對象,實(shí)現(xiàn)了在Java應(yīng)用中方便地使用Redis,本文給大家介紹Java中Redisson的原理,感興趣的朋友跟隨小編一起看看吧
    2025-11-11
  • SpringBoot配置Ollama實(shí)現(xiàn)本地部署DeepSeek

    SpringBoot配置Ollama實(shí)現(xiàn)本地部署DeepSeek

    本文主要介紹了在本地環(huán)境中使用?Ollama?配置?DeepSeek?模型,并在?IntelliJ?IDEA?中創(chuàng)建一個(gè)?Spring?Boot?項(xiàng)目來調(diào)用該模型,文中通過圖文示例介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-03-03
  • JAVA中String介紹及常見面試題小結(jié)

    JAVA中String介紹及常見面試題小結(jié)

    這篇文章主要介紹了JAVA中String介紹及常見面試題,在java面試中經(jīng)常會被面試官問到,小編通過實(shí)例代碼相結(jié)合給大家詳細(xì)介紹,需要的朋友可以參考下
    2020-02-02
  • SpringBoot如何配置tomcat access日志

    SpringBoot如何配置tomcat access日志

    access日志記錄了每一個(gè)HTTP請求的信息,包括請求的來源、請求的資源、響應(yīng)狀態(tài)碼等,常常用來做數(shù)據(jù)統(tǒng)計(jì)、性能監(jiān)控,比如通過分析訪問日志,可以發(fā)現(xiàn)性能瓶頸和優(yōu)化機(jī)會,提升應(yīng)用的響應(yīng)速度等,這篇文章主要介紹了SpringBoot配置tomcat access日志,需要的朋友可以參考下
    2024-05-05
  • 詳解mybatis generator代碼生成器的使用

    詳解mybatis generator代碼生成器的使用

    MyBatis Generator(MBG)是MyBatis MyBatis 和iBATIS的代碼生成器。這篇文章主要介紹了mybatis generator代碼生成器的使用,需要的朋友可以參考下
    2021-09-09
  • maven中springboot-maven-plugin的5種打包方式

    maven中springboot-maven-plugin的5種打包方式

    本文主要介紹了maven中springboot-maven-plugin的5種打包方式,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2024-09-09
  • Java 基礎(chǔ)--Arrays工具類詳解

    Java 基礎(chǔ)--Arrays工具類詳解

    這篇文章主要介紹了Java Arrays工具類用法,結(jié)合實(shí)例形式分析了java Arrays工具類針對數(shù)組元素修改、復(fù)制、排序等操作使用技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2021-09-09
  • springboot中使用Hibernate-Validation校驗(yàn)參數(shù)詳解

    springboot中使用Hibernate-Validation校驗(yàn)參數(shù)詳解

    這篇文章主要為大家介紹了springboot中使用Hibernate-Validation校驗(yàn)參數(shù)詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-07-07
  • 解析Orika的MapperFacade 屬性賦值的使用問題

    解析Orika的MapperFacade 屬性賦值的使用問題

    在我們實(shí)際開發(fā)中,常常會有對象與對象之間的轉(zhuǎn)化,或者把一個(gè)對象的數(shù)據(jù)轉(zhuǎn)化到另一個(gè)數(shù)據(jù)之中,如果我們手動的一個(gè)一個(gè)的set就會比較麻煩,代碼段看起來也會比較長。而Orika的MapperFacade就是解決這個(gè)問題的,實(shí)現(xiàn)對象屬性的復(fù)制
    2021-12-12
  • Java利用多線程復(fù)制文件

    Java利用多線程復(fù)制文件

    這篇文章主要介紹了Java利用多線程復(fù)制文件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-09-09

最新評論

平南县| 河池市| 裕民县| 迁安市| 光山县| 二连浩特市| 如皋市| 皮山县| 行唐县| 邢台县| 石家庄市| 宣城市| 罗定市| 大方县| 平凉市| 子洲县| 广河县| 姚安县| 虎林市| 古交市| 两当县| 深州市| 建宁县| 安溪县| 汾阳市| 隆林| 开阳县| 太康县| 襄垣县| 安塞县| 鹤峰县| 临漳县| 罗江县| 日照市| 寻甸| 德兴市| 达拉特旗| 台北县| 禹城市| 岳普湖县| 县级市|