Условное слияние наблюдений - PullRequest
0 голосов
/ 04 декабря 2018

Я запрашиваю файл через http.Этот файл содержит информацию о других файлах, которые необходимо включить в основной файл.В моем случае это xsd-файлы с импортом, но я думаю, что это может быть что угодно.

Вы можете просмотреть код здесь: https://stackblitz.com/edit/angular-ugtaka
Я включил некоторые результаты консоли, которые показывают, что каждый запрос будетвыполнено, но в итоге моя наблюдаемая не выдает никаких значений.

Существует 3 xsd файла, которые структурированы таким образом: main.xsd импортирует sub1.xsd и sub2.xsd, sub2.xsd импортирует sub1.xsd.
Должен быть запрос для каждого файла, и sub1.xsd будет запрашиваться два раза.

myObservable
.pipe(mergeMap((data) => {

    if (data.import !== undefined) {
      const requests: Observable<any>[] = [];
      if (data.import instanceof Array) {
        for (const xmlImport of data.import) {
          const localPath = `/assets/${xmlImport.path}`;
          requests.push(this.getXsdSchema(localPath));
        }
        const forked = combineLatest(requests);
        return forked;
      } else {
        const localPath = `/assets/${import.path}`;
        return this.getXsdSchema(localPath);
      }
    }
    const myEmpty = never();
    return myEmpty;
  }))

Это не фактический код из демо.Я попытался сделать его немного короче и включить только ту часть, где может быть проблема.
myObservable - это проанализированный xsd-файл, содержащий операторы импорта, которые должны быть объединены с наблюдаемой.Если существует более одного импорта, я пытаюсь объединить их с integraLatest и объединить их с основной наблюдаемой, используя оператор mergeMap .
Если есть только один, я могу пропустить ОбъединитьПоследний часть и вернуть его напрямую, и если его нет, я попытался использовать никогда и пусто .

В конце я хотел быесть поток, который содержит все наблюдаемые.Я хотел бы использовать оператор Reduce, чтобы объединить их в один объект, который содержит информацию обо всех файлах.

Если я заменю never () в строке 68 на что-то вроде из («ничего») наблюдаемое будет выдавать значения, но фактическая информация не включена.

Ответы [ 2 ]

0 голосов
/ 04 декабря 2018

Ответ PierreDuc довольно крутой, потому что он создал проанализированное дерево XML.Мой другой подход, который похож на исходный код, за исключением того, что я использовал расширение, чтобы сделать рекурсивный вызов и в конце уменьшить данные до плоского массива, содержащего четыре элемента.Кстати, xml2js.parseString нужно обернуть в Observable

import { Component } from '@angular/core';
import { HttpHeaders, HttpClient } from '@angular/common/http';
import * as xml2js from 'xml2js';
import { Observable, forkJoin, empty, never, combineLatest, of, merge,zip ,concat} from 'rxjs';
import { switchMap, expand,mergeMap, map, catchError, tap,last ,scan,reduce} from 'rxjs/operators';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';

  constructor(private http: HttpClient) {
    this.getXsdSchema('/assets/main.xsd').subscribe((data) => {
      console.log('data',data);
    });
  }


  public getXsdSchema(path: string): Observable<any> {
    return this.http.get(path, {
      responseType: 'text',
      headers: new HttpHeaders({
        Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
      })
    })
      .pipe(
        mergeMap(data => {
        let rtrn: any;
        return new Observable(obs=>{

        xml2js.parseString(data, {
          explicitArray: false,
          tagNameProcessors: [function (name) {
            // replace ":" with "_" for all tag names to make sure that they can be accessed within javascript
            return name.replace(/:/g, '_');
          }]
        }, (err, xmlObject) => {
          if (err) {
            obs.error(err)
          } else {
            obs.next(xmlObject)
            obs.complete()
          }
        });

        })
      })
      ,expand((data)=>{
      if (data.xs_schema&&data.xs_schema.xs_import !== undefined) {
          const requests: Observable<any>[] = [];

          if (data.xs_schema.xs_import instanceof Array) {
            for (const xmlImport of data.xs_schema.xs_import) {
              const localPath = `/assets/${xmlImport.$.schemaLocation}`;
              requests.push(this.getXsdSchema(localPath));
              //return this.getXsdSchema(localPath)
            }
            const forked = merge(...requests);
            return forked;
          } else {
            const localPath = `/assets/${data.xs_schema.xs_import.$.schemaLocation}`;
            return this.getXsdSchema(localPath);
          }
        }
        return empty()
      })
      ,reduce((acc,curr)=>acc.concat(curr),[])

      )
  }

}
0 голосов
/ 04 декабря 2018

Проблема в том, что путь xs_schema.xs_schema.xs_import.Этого можно избежать, используя опцию explicitRoot: false.Я раздвоил ваш stackblitz , в основном он заменяет данные файлов в свойстве xs_import:

public getXsdSchema(path: string): Observable<any> {
  //console.log(path);
  return this.http.get(path, {
    responseType: 'text',
    headers: new HttpHeaders({
      Accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8'
    })
  })
    .pipe(concatMap(data => {
      return new Promise((resolve, reject) => {
        xml2js.parseString(data, {
          explicitArray: false,
          explicitRoot: false,
          tagNameProcessors: [function (name) {
            // replace ":" with "_" for all tag names to make sure that they can be accessed within javascript
            return name.replace(/:/g, '_');
          }]
        }, (err, xmlObject) => {
          err ? reject(err) : resolve(xmlObject);
        });
      });
    }),
    concatMap((data: any) => {
      if (data.xs_import) {
        if (!Array.isArray(data.xs_import)) {
          data.xs_import = data.xs_import ? [data.xs_import] : [];
        }

        return zip(...data.xs_import.map((xmlImport) => 
          this.getXsdSchema(`/assets/${xmlImport.$.schemaLocation}`)
        )).pipe(
          map((imports) => {
            data.xs_import = imports;
            return data;
          })
        );

      } else {
        return of(data);
      }
    }))
}

FYI: вы можете объединять операторы, разделяя их сзапятая внутри вызова канала.

В результате:

$: Object
xs_element: Object
xs_import: Array[2]
  0: Object
    $: Object
    xs_simpleType: Object
  1: Object
    $: Object
    xs_complexType: Object
    xs_import: Array[1]
      0: Object
        $: Object
        xs_simpleType: Object
...